Exemplo n.º 1
0
/* this is called after the connection on the server side by us to
   check other aspects about the connection and obtain the
   securityName from the remote certificate. */
int netsnmp_tlsbase_extract_security_name (SSL * ssl, _netsnmpTLSBaseData * tlsdata)
{
    netsnmp_container *chain_maps;

    netsnmp_cert_map *cert_map, *peer_cert;

    netsnmp_iterator *itr;

    int rc;

    netsnmp_assert_or_return (ssl != NULL, SNMPERR_GENERR);
    netsnmp_assert_or_return (tlsdata != NULL, SNMPERR_GENERR);

    if (NULL == (chain_maps = netsnmp_openssl_get_cert_chain (ssl)))
        return SNMPERR_GENERR;
    /*
     * map fingerprints to mapping entries
     */
    rc = netsnmp_cert_get_secname_maps (chain_maps);
    if ((-1 == rc) || (CONTAINER_SIZE (chain_maps) == 0))
    {
        netsnmp_cert_map_container_free (chain_maps);
        return SNMPERR_GENERR;
    }

    /*
     * change container to sorted (by clearing unsorted option), then
     * iterate over it until we find a map that returns a secname.
     */
    CONTAINER_SET_OPTIONS (chain_maps, 0, rc);
    itr = CONTAINER_ITERATOR (chain_maps);
    if (NULL == itr)
    {
        snmp_log (LOG_ERR, "could not get iterator for secname fingerprints\n");
        netsnmp_cert_map_container_free (chain_maps);
        return SNMPERR_GENERR;
    }
    peer_cert = cert_map = ITERATOR_FIRST (itr);
    for (; !tlsdata->securityName && cert_map; cert_map = ITERATOR_NEXT (itr))
        tlsdata->securityName = netsnmp_openssl_extract_secname (cert_map, peer_cert);
    ITERATOR_RELEASE (itr);

    netsnmp_cert_map_container_free (chain_maps);

    return (tlsdata->securityName ? SNMPERR_SUCCESS : SNMPERR_GENERR);
}
Exemplo n.º 2
0
int netsnmp_tlsbase_config (struct netsnmp_transport_s *t, const char *token, const char *value)
{
    _netsnmpTLSBaseData *tlsdata;

    netsnmp_assert_or_return (t != NULL, -1);
    netsnmp_assert_or_return (t->data != NULL, -1);

    tlsdata = t->data;

    if ((strcmp (token, "localCert") == 0) || (strcmp (token, "our_identity") == 0))
    {
        SNMP_FREE (tlsdata->our_identity);
        tlsdata->our_identity = strdup (value);
        DEBUGMSGT (("tls:config", "our identity %s\n", value));
    }

    if ((strcmp (token, "peerCert") == 0) || (strcmp (token, "their_identity") == 0))
    {
        SNMP_FREE (tlsdata->their_identity);
        tlsdata->their_identity = strdup (value);
        DEBUGMSGT (("tls:config", "their identity %s\n", value));
    }

    if ((strcmp (token, "peerHostname") == 0) || (strcmp (token, "their_hostname") == 0))
    {
        SNMP_FREE (tlsdata->their_hostname);
        tlsdata->their_hostname = strdup (value);
    }

    if ((strcmp (token, "trust_cert") == 0) || (strcmp (token, "trustCert") == 0))
    {
        SNMP_FREE (tlsdata->trust_cert);
        tlsdata->trust_cert = strdup (value);
    }

    return SNMPERR_SUCCESS;
}
netsnmp_transport *
netsnmp_udpipv4base_transport(struct sockaddr_in *addr, int local)
{
    netsnmp_transport *t = NULL;
    int             rc = 0, rc2;
    char           *str = NULL;
    char           *client_socket = NULL;
    netsnmp_indexed_addr_pair addr_pair;
    socklen_t       local_addr_len;

    if (addr == NULL || addr->sin_family != AF_INET) {
        return NULL;
    }

    memset(&addr_pair, 0, sizeof(netsnmp_indexed_addr_pair));
    memcpy(&(addr_pair.remote_addr), addr, sizeof(struct sockaddr_in));

    t = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
    netsnmp_assert_or_return(t != NULL, NULL);

    str = netsnmp_udp_fmtaddr(NULL, (void *)&addr_pair,
                                 sizeof(netsnmp_indexed_addr_pair));
    DEBUGMSGTL(("netsnmp_udpbase", "open %s %s\n", local ? "local" : "remote",
                str));
    free(str);

    t->sock = socket(PF_INET, SOCK_DGRAM, 0);
    DEBUGMSGTL(("UDPBase", "openned socket %d as local=%d\n", t->sock, local)); 
    if (t->sock < 0) {
        netsnmp_transport_free(t);
        return NULL;
    }

    _netsnmp_udp_sockopt_set(t->sock, local);

    if (local) {
        /*
         * This session is inteneded as a server, so we must bind on to the
         * given IP address, which may include an interface address, or could
         * be INADDR_ANY, but certainly includes a port number.
         */

      t->local = (u_char *) malloc(6);
        if (t->local == NULL) {
            netsnmp_transport_free(t);
            return NULL;
        }
        memcpy(t->local, (u_char *) & (addr->sin_addr.s_addr), 4);
        t->local[4] = (htons(addr->sin_port) & 0xff00) >> 8;
        t->local[5] = (htons(addr->sin_port) & 0x00ff) >> 0;
        t->local_length = 6;

#if defined(linux) && defined(IP_PKTINFO)
        { 
            int sockopt = 1;
            if (setsockopt(t->sock, SOL_IP, IP_PKTINFO, &sockopt, sizeof sockopt) == -1) {
                DEBUGMSGTL(("netsnmp_udpbase", "couldn't set IP_PKTINFO: %s\n",
                    strerror(errno)));
                netsnmp_transport_free(t);
                return NULL;
            }
            DEBUGMSGTL(("netsnmp_udpbase", "set IP_PKTINFO\n"));
        }
#endif
        rc = bind(t->sock, (struct sockaddr *) addr,
                  sizeof(struct sockaddr));
        if (rc != 0) {
            netsnmp_socketbase_close(t);
            netsnmp_transport_free(t);
            return NULL;
        }
        t->data = NULL;
        t->data_length = 0;
    } else {
Exemplo n.º 4
0
/* this is called after the connection on the client side by us to check
   other aspects about the connection */
int netsnmp_tlsbase_verify_server_cert (SSL * ssl, _netsnmpTLSBaseData * tlsdata)
{
    /* XXX */
    X509 *remote_cert;

    char *check_name;

    int ret;

    netsnmp_assert_or_return (ssl != NULL, SNMPERR_GENERR);
    netsnmp_assert_or_return (tlsdata != NULL, SNMPERR_GENERR);

    if (NULL == (remote_cert = SSL_get_peer_certificate (ssl)))
    {
        /* no peer cert */
        DEBUGMSGTL (("tls_x509:verify", "remote connection provided no certificate (yet)\n"));
        return SNMPERR_TLS_NO_CERTIFICATE;
    }

    /* make sure that the fingerprint matches */
    ret = _netsnmp_tlsbase_verify_remote_fingerprint (remote_cert, tlsdata, 1);
    switch (ret)
    {
        case VERIFIED_FINGERPRINT:
            return SNMPERR_SUCCESS;

        case FAILED_FINGERPRINT_VERIFY:
            return SNMPERR_GENERR;

        case NO_FINGERPRINT_AVAILABLE:
            if (tlsdata->their_hostname && tlsdata->their_hostname[0] != '\0')
            {
                GENERAL_NAMES *onames;

                const GENERAL_NAME *oname = NULL;

                int i, j;

                int count;

                char buf[SPRINT_MAX_LEN];

                int is_wildcarded = 0;

                char *compare_to;

                /* see if the requested hostname has a wildcard prefix */
                if (strncmp (tlsdata->their_hostname, "*.", 2) == 0)
                {
                    is_wildcarded = 1;
                    compare_to = tlsdata->their_hostname + 2;
                }
                else
                {
                    compare_to = tlsdata->their_hostname;
                }

                /* if the hostname we were expecting to talk to matches
                   the cert, then we can accept this connection. */

                /* check against the DNS subjectAltName */
                onames = (GENERAL_NAMES *) X509_get_ext_d2i (remote_cert, NID_subject_alt_name, NULL, NULL);
                if (NULL != onames)
                {
                    count = sk_GENERAL_NAME_num (onames);

                    for (i = 0; i < count; ++i)
                    {
                        oname = sk_GENERAL_NAME_value (onames, i);
                        if (GEN_DNS == oname->type)
                        {

                            /* get the value */
                            ASN1_STRING_to_UTF8 ((unsigned char **) &check_name, oname->d.ia5);

                            /* convert to lowercase for comparisons */
                            for (j = 0; *check_name && j < sizeof (buf) - 1; ++check_name, ++j)
                            {
                                if (isascii (*check_name))
                                    buf[j] = tolower (0xFF & *check_name);
                            }
                            if (j < sizeof (buf))
                                buf[j] = '\0';
                            check_name = buf;

                            if (is_wildcarded)
                            {
                                /* we *only* allow passing till the first '.' */
                                /* ie *.example.com can't match a.b.example.com */
                                check_name = strchr (check_name, '.') + 1;
                            }

                            DEBUGMSGTL (("tls_x509:verify", "checking subjectAltname of dns:%s\n", check_name));
                            if (strcmp (compare_to, check_name) == 0)
                            {

                                DEBUGMSGTL (("tls_x509:verify", "Successful match on a subjectAltname of dns:%s\n",
                                             check_name));
                                return SNMPERR_SUCCESS;
                            }
                        }
                    }
                }

                /* check the common name for a match */
                check_name = netsnmp_openssl_cert_get_commonName (remote_cert, NULL, NULL);

                if (is_wildcarded)
                {
                    /* we *only* allow passing till the first '.' */
                    /* ie *.example.com can't match a.b.example.com */
                    check_name = strchr (check_name, '.') + 1;
                }

                if (strcmp (compare_to, check_name) == 0)
                {
                    DEBUGMSGTL (("tls_x509:verify", "Successful match on a common name of %s\n", check_name));
                    return SNMPERR_SUCCESS;
                }

                snmp_log (LOG_ERR, "No matching names in the certificate to match the expected %s\n",
                          tlsdata->their_hostname);
                return SNMPERR_GENERR;

            }
            /* XXX: check for hostname match instead */
            snmp_log (LOG_ERR, "Can not verify a remote server identity without configuration\n");
            return SNMPERR_GENERR;
    }
    DEBUGMSGTL (("tls_x509:verify", "shouldn't get here\n"));
    return SNMPERR_GENERR;
}
Exemplo n.º 5
0
int
tsm_process_in_msg(struct snmp_secmod_incoming_params *parms)
{
    u_char type_value;
    size_t remaining;
    u_char *data_ptr;
    netsnmp_tmStateReference *tmStateRef;
    netsnmp_tsmSecurityReference *tsmSecRef;
    u_char          ourEngineID[SNMP_MAX_ENG_SIZE];
    static size_t   ourEngineID_len = sizeof(ourEngineID);
    
    /* Section 5.2, step 1: Set the securityEngineID to the local
       snmpEngineID. */
    ourEngineID_len =
        snmpv3_get_engineID((u_char*) ourEngineID, ourEngineID_len);
    netsnmp_assert_or_return(ourEngineID_len != 0 &&
                             ourEngineID_len <= *parms->secEngineIDLen,
                             SNMPERR_GENERR);
    memcpy(parms->secEngineID, ourEngineID, *parms->secEngineIDLen);

    /* Section 5.2, step 2: If tmStateReference does not refer to a
       cache containing values for tmTransportDomain,
       tmTransportAddress, tmSecurityName, and
       tmTransportSecurityLevel, then the snmpTsmInvalidCaches counter
       is incremented, an error indication is returned to the calling
       module, and Security Model processing stops for this
       message. */
    if (!parms->pdu->transport_data ||
        sizeof(netsnmp_tmStateReference) !=
        parms->pdu->transport_data_length) {
        /* if we're not coming in over a proper transport; bail! */
        DEBUGMSGTL(("tsm","improper transport data\n"));
        return -1;
    }
    tmStateRef = (netsnmp_tmStateReference *) parms->pdu->transport_data;
    parms->pdu->transport_data = NULL;

    if (tmStateRef == NULL ||
        /* not needed: tmStateRef->transportDomain == NULL || */
        /* not needed: tmStateRef->transportAddress == NULL || */
        tmStateRef->securityName[0] == '\0'
        ) {
        snmp_increment_statistic(STAT_TSM_SNMPTSMINVALIDCACHES);
        return SNMPERR_GENERR;
    }

    /* Section 5.2, step 3: Copy the tmSecurityName to securityName. */
    if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
                               NETSNMP_DS_LIB_TSM_USE_PREFIX)) {
        /* Section 5.2, step 3:
          If the snmpTsmConfigurationUsePrefix object is set to true, then
          use the tmTransportDomain to look up the corresponding prefix.
        */
        const char *prefix = NULL;
        /*
          possibilities:
           |--------------------+-------|
           | snmpTLSTCPDomain   | tls:  |
           | snmpDTLSUDPDomain  | dtls: |
           | snmpSSHDomain      | ssh:  |
           |--------------------+-------|
        */
        
        if (tmStateRef->transportDomain == NULL) {
            /* XXX: snmpTsmInvalidCaches++ ??? */
            return SNMPERR_GENERR;
        }

        /* XXX: cache in session! */
#ifdef NETSNMP_TRANSPORT_SSH_DOMAIN
        if (netsnmp_oid_equals(netsnmp_snmpSSHDomain,
                               netsnmp_snmpSSHDomain_len,
                               tmStateRef->transportDomain,
                               tmStateRef->transportDomainLen) == 0) {
            prefix = "ssh";
        }
#endif /*  NETSNMP_TRANSPORT_SSH_DOMAIN */

#ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN
        if (netsnmp_oid_equals(netsnmpDTLSUDPDomain,
                               netsnmpDTLSUDPDomain_len,
                               tmStateRef->transportDomain,
                               tmStateRef->transportDomainLen) == 0) {
            
            prefix = "dtls";
        }
#endif /* NETSNMP_TRANSPORT_DTLSUDP_DOMAIN */

#ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN
        if (netsnmp_oid_equals(netsnmpTLSTCPDomain,
                               netsnmpTLSTCPDomain_len,
                               tmStateRef->transportDomain,
                               tmStateRef->transportDomainLen) == 0) {
            
            prefix = "tls";
        }
#endif /* NETSNMP_TRANSPORT_TLSTCP_DOMAIN */

        /* Section 5.2, step 3:
          If the prefix lookup fails for any reason, then the
          snmpTsmUnknownPrefixes counter is incremented, an error
          indication is returned to the calling module, and message
          processing stops.
        */
        if (prefix == NULL) {
            snmp_increment_statistic(STAT_TSM_SNMPTSMUNKNOWNPREFIXES);
            return SNMPERR_GENERR;
        }

        /* Section 5.2, step 3:
          If the lookup succeeds but the prefix length is less than 1 or
          greater than 4 octets, then the snmpTsmInvalidPrefixes counter
          is incremented, an error indication is returned to the calling
          module, and message processing stops.
        */
#ifdef NOT_USING_HARDCODED_PREFIXES
        /* the above code actually ensures this will never happen as
           we don't support a dynamic prefix database where this might
           happen. */
        if (strlen(prefix) < 1 || strlen(prefix) > 4) {
            /* XXX: snmpTsmInvalidPrefixes++ */
            return SNMPERR_GENERR;
        }
#endif
        
        /* Section 5.2, step 3:
          Set the securityName to be the concatenation of the prefix, a
          ':' character (US-ASCII 0x3a), and the tmSecurityName.
        */
        snprintf(parms->secName, *parms->secNameLen,
                 "%s:%s", prefix, tmStateRef->securityName);
    } else {
        /* if the use prefix flag wasn't set, do a straight copy */
        strncpy(parms->secName, tmStateRef->securityName, *parms->secNameLen);
    }

    /* set the length of the security name */
    *parms->secNameLen = strlen(parms->secName);
    DEBUGMSGTL(("tsm", "user: %s/%d\n", parms->secName, (int)*parms->secNameLen));

    /* Section 5.2 Step 4:
       Compare the value of tmTransportSecurityLevel in the
       tmStateReference cache to the value of the securityLevel
       parameter passed in the processIncomingMsg ASI.  If securityLevel
       specifies privacy (Priv) and tmTransportSecurityLevel specifies
       no privacy (noPriv), or if securityLevel specifies authentication
       (auth) and tmTransportSecurityLevel specifies no authentication
       (noAuth) was provided by the Transport Model, then the
       snmpTsmInadequateSecurityLevels counter is incremented, an error
       indication (unsupportedSecurityLevel) together with the OID and
       value of the incremented counter is returned to the calling
       module, and Transport Security Model processing stops for this
       message.*/
    if (parms->secLevel > tmStateRef->transportSecurityLevel) {
        snmp_increment_statistic(STAT_TSM_SNMPTSMINADEQUATESECURITYLEVELS);
        DEBUGMSGTL(("tsm", "inadequate security level: %d\n", parms->secLevel));
        /* net-snmp returns error codes not OIDs, which are dealt with later */
        return SNMPERR_UNSUPPORTED_SEC_LEVEL;
    }

    /* Section 5.2 Step 5
       The tmStateReference is cached as cachedSecurityData so that a
       possible response to this message will use the same security
       parameters.  Then securityStateReference is set for subsequent
       references to this cached data.
    */
    if (NULL == *parms->secStateRef) {
        tsmSecRef = SNMP_MALLOC_TYPEDEF(netsnmp_tsmSecurityReference);
    } else {
        tsmSecRef = *parms->secStateRef;
    }

    netsnmp_assert_or_return(NULL != tsmSecRef, SNMPERR_GENERR);

    *parms->secStateRef = tsmSecRef;
    tsmSecRef->tmStateRef = tmStateRef;

    /* If this did not come through a tunneled connection, this
       security model is inappropriate (and would be a HUGE security
       hole to assume otherwise).  This is functionally a double check
       since the pdu wouldn't have transport data otherwise.  But this
       is safer though is functionally an extra step beyond the TSM
       RFC. */
    DEBUGMSGTL(("tsm","checking how we got here\n"));
    if (!(parms->pdu->flags & UCD_MSG_FLAG_TUNNELED)) {
        DEBUGMSGTL(("tsm","  pdu not tunneled\n"));
        if (!(parms->sess->flags & NETSNMP_TRANSPORT_FLAG_TUNNELED)) {
            DEBUGMSGTL(("tsm","  session not tunneled\n"));
            return SNMPERR_USM_AUTHENTICATIONFAILURE;
        }
        DEBUGMSGTL(("tsm","  but session is tunneled\n"));
    } else {
        DEBUGMSGTL(("tsm","  tunneled\n"));
    }

    /* Section 5.2, Step 6:
       The scopedPDU component is extracted from the wholeMsg. */
    /*
     * Eat the first octet header.
     */
    remaining = parms->wholeMsgLen - (parms->secParams - parms->wholeMsg);
    if ((data_ptr = asn_parse_sequence(parms->secParams, &remaining,
                                        &type_value,
                                        (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                         ASN_OCTET_STR),
                                        "tsm first octet")) == NULL) {
        /*
         * RETURN parse error 
         */
        return SNMPERR_ASN_PARSE_ERR;
    }
    
    *parms->scopedPdu = data_ptr;
    *parms->scopedPduLen = parms->wholeMsgLen - (data_ptr - parms->wholeMsg);

    /* Section 5.2, Step 7:
       The maxSizeResponseScopedPDU is calculated.  This is the maximum
       size allowed for a scopedPDU for a possible Response message.
     */
    *parms->maxSizeResponse = parms->maxMsgSize; /* XXX */

    /* Section 5.2, Step 8:
       The statusInformation is set to success and a return is made to
       the calling module passing back the OUT parameters as specified
       in the processIncomingMsg ASI.
    */
    return SNMPERR_SUCCESS;
}
Exemplo n.º 6
0
int
tsm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
{
    u_char         **wholeMsg = parms->wholeMsg;
    size_t	   *offset = parms->wholeMsgOffset;
    int rc;
    
    size_t         *wholeMsgLen = parms->wholeMsgLen;
    netsnmp_tsmSecurityReference *tsmSecRef;
    netsnmp_tmStateReference *tmStateRef;
    int             tmStateRefLocal = 0;
    
    DEBUGMSGTL(("tsm", "Starting TSM processing\n"));

    /* if we have this, then this message to be sent is in response to
       something that came in earlier and the tsmSecRef was created by
       the tsm_process_in_msg. */
    tsmSecRef = parms->secStateRef;
    
    if (tsmSecRef) {
        /* 4.2, step 1: If there is a securityStateReference (Response
           or Report message), then this Security Model uses the
           cached information rather than the information provided by
           the ASI. */

        /* 4.2, step 1: Extract the tmStateReference from the
           securityStateReference cache. */
        netsnmp_assert_or_return(NULL != tsmSecRef->tmStateRef, SNMPERR_GENERR);
        tmStateRef = tsmSecRef->tmStateRef;

        /* 4.2 step 1: Set the tmRequestedSecurityLevel to the value
           of the extracted tmTransportSecurityLevel. */
        tmStateRef->requestedSecurityLevel = tmStateRef->transportSecurityLevel;

        /* 4.2 step 1: Set the tmSameSecurity parameter in the
           tmStateReference cache to true. */
        tmStateRef->sameSecurity = NETSNMP_TM_USE_SAME_SECURITY;

        /* 4.2 step 1: The cachedSecurityData for this message can
           now be discarded. */
        SNMP_FREE(parms->secStateRef);
    } else {
        /* 4.2, step 2: If there is no securityStateReference (e.g., a
           Request-type or Notification message), then create a
           tmStateReference cache. */
        tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference);
        netsnmp_assert_or_return(NULL != tmStateRef, SNMPERR_GENERR);
        tmStateRefLocal = 1;

        /* XXX: we don't actually use this really in our implementation */
        /* 4.2, step 2: Set tmTransportDomain to the value of
           transportDomain, tmTransportAddress to the value of
           transportAddress */

        /* 4.2, step 2: and tmRequestedSecurityLevel to the value of
           securityLevel. */
        tmStateRef->requestedSecurityLevel = parms->secLevel;

        /* 4.2, step 2: Set the transaction-specific tmSameSecurity
           parameter to false. */
        tmStateRef->sameSecurity = NETSNMP_TM_SAME_SECURITY_NOT_REQUIRED;

        if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
                                   NETSNMP_DS_LIB_TSM_USE_PREFIX)) {
            /* XXX: probably shouldn't be a hard-coded list of
               supported transports */
            /* 4.2, step 2: If the snmpTsmConfigurationUsePrefix
               object is set to true, then use the transportDomain to
               look up the corresponding prefix. */
            const char *prefix;
            if (strncmp("ssh:",parms->session->peername,4) == 0)
                prefix = "ssh:";
            else if (strncmp("dtls:",parms->session->peername,5) == 0)
                prefix = "dtls:";
            else if (strncmp("tls:",parms->session->peername,4) == 0)
                prefix = "tls:";
            else {
                /* 4.2, step 2: If the prefix lookup fails for any
                   reason, then the snmpTsmUnknownPrefixes counter is
                   incremented, an error indication is returned to the
                   calling module, and message processing stops. */
                snmp_increment_statistic(STAT_TSM_SNMPTSMUNKNOWNPREFIXES);
                SNMP_FREE(tmStateRef);
                return SNMPERR_GENERR;
            }

            /* 4.2, step 2: If the lookup succeeds, but there is no
               prefix in the securityName, or the prefix returned does
               not match the prefix in the securityName, or the length
               of the prefix is less than 1 or greater than 4 US-ASCII
               alpha-numeric characters, then the
               snmpTsmInvalidPrefixes counter is incremented, an error
               indication is returned to the calling module, and
               message processing stops. */
            if (strchr(parms->secName, ':') == 0 ||
                strlen(prefix)+1 >= parms->secNameLen ||
                strncmp(parms->secName, prefix, strlen(prefix)) != 0 ||
                parms->secName[strlen(prefix)] != ':') {
                /* Note: since we're assiging the prefixes above the
                   prefix lengths always meet the 1-4 criteria */
                snmp_increment_statistic(STAT_TSM_SNMPTSMINVALIDPREFIXES);
                SNMP_FREE(tmStateRef);
                return SNMPERR_GENERR;
            }

            /* 4.2, step 2: Strip the transport-specific prefix and
               trailing ':' character (US-ASCII 0x3a) from the
               securityName.  Set tmSecurityName to the value of
               securityName. */
            memcpy(tmStateRef->securityName,
                   parms->secName + strlen(prefix) + 1,
                   parms->secNameLen - strlen(prefix) - 1);
            tmStateRef->securityNameLen = parms->secNameLen - strlen(prefix) -1;
        } else {
            /* 4.2, step 2: If the snmpTsmConfigurationUsePrefix object is
               set to false, then set tmSecurityName to the value
               of securityName. */
            memcpy(tmStateRef->securityName, parms->secName,
                   parms->secNameLen);
            tmStateRef->securityNameLen = parms->secNameLen;
        }
    }

    /* truncate the security name with a '\0' for safety */
    tmStateRef->securityName[tmStateRef->securityNameLen] = '\0';

    /* 4.2, step 3: Set securityParameters to a zero-length OCTET
     *  STRING ('0400').
     */
    DEBUGDUMPHEADER("send", "tsm security parameters");
    rc = asn_realloc_rbuild_header(wholeMsg, wholeMsgLen, offset, 1,
                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
                                             | ASN_OCTET_STR), 0);
    DEBUGINDENTLESS();
    if (rc == 0) {
        DEBUGMSGTL(("tsm", "building msgSecurityParameters failed.\n"));
        if (tmStateRefLocal)
            SNMP_FREE(tmStateRef);
        return SNMPERR_TOO_LONG;
    }
    
    /* 4.2, step 4: Combine the message parts into a wholeMsg and
       calculate wholeMsgLength.
     */
    while ((*wholeMsgLen - *offset) < parms->globalDataLen) {
        if (!asn_realloc(wholeMsg, wholeMsgLen)) {
            DEBUGMSGTL(("tsm", "building global data failed.\n"));
            if (tmStateRefLocal)
                SNMP_FREE(tmStateRef);
            return SNMPERR_TOO_LONG;
        }
    }

    *offset += parms->globalDataLen;
    memcpy(*wholeMsg + *wholeMsgLen - *offset,
           parms->globalData, parms->globalDataLen);

    /* 4.2, step 5: The wholeMsg, wholeMsgLength, securityParameters,
       and tmStateReference are returned to the calling Message
       Processing Model with the statusInformation set to success. */

    /* For the Net-SNMP implemantion that actually means we start
       encoding the full packet sequence from here before returning it */

    /*
     * Total packet sequence.  
     */
    rc = asn_realloc_rbuild_sequence(wholeMsg, wholeMsgLen, offset, 1,
                                     (u_char) (ASN_SEQUENCE |
                                               ASN_CONSTRUCTOR), *offset);
    if (rc == 0) {
        DEBUGMSGTL(("tsm", "building master packet sequence failed.\n"));
        if (tmStateRefLocal)
            SNMP_FREE(tmStateRef);
        return SNMPERR_TOO_LONG;
    }

    if (parms->pdu->transport_data &&
        parms->pdu->transport_data != tmStateRef) {
        snmp_log(LOG_ERR, "tsm: needed to free transport data\n");
        SNMP_FREE(parms->pdu->transport_data);
    }

    /* put the transport state reference into the PDU for the transport */
    parms->pdu->transport_data = netsnmp_memdup(tmStateRef, sizeof(*tmStateRef));
    if (!parms->pdu->transport_data)
        snmp_log(LOG_ERR, "tsm: malloc failure\n");
    parms->pdu->transport_data_length = sizeof(*tmStateRef);

    if (tmStateRefLocal)
        SNMP_FREE(tmStateRef);
    DEBUGMSGTL(("tsm", "TSM processing completed.\n"));
    return SNMPERR_SUCCESS;
}
Exemplo n.º 7
0
netsnmp_transport *
netsnmp_tlstcp_open(netsnmp_transport *t)
{
    _netsnmpTLSBaseData *tlsdata;
    BIO *bio;
    SSL_CTX *ctx;
    SSL *ssl;
    int rc = 0;
    _netsnmp_verify_info *verify_info;

    netsnmp_assert_or_return(t != NULL, NULL);
    netsnmp_assert_or_return(t->data != NULL, NULL);
    netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length,
                             NULL);

    tlsdata = t->data;

    if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) {
        /* Is the client */

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
         *    1)  The snmpTlstmSessionOpens counter is incremented.
         */
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS);

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
          2)  The client selects the appropriate certificate and cipher_suites
              for the key agreement based on the tmSecurityName and the
              tmRequestedSecurityLevel for the session.  For sessions being
              established as a result of a SNMP-TARGET-MIB based operation, the
              certificate will potentially have been identified via the
              snmpTlstmParamsTable mapping and the cipher_suites will have to
              be taken from system-wide or implementation-specific
              configuration.  If no row in the snmpTlstmParamsTable exists then
              implementations MAY choose to establish the connection using a
              default client certificate available to the application.
              Otherwise, the certificate and appropriate cipher_suites will
              need to be passed to the openSession() ASI as supplemental
              information or configured through an implementation-dependent
              mechanism.  It is also implementation-dependent and possibly
              policy-dependent how tmRequestedSecurityLevel will be used to
              influence the security capabilities provided by the (D)TLS
              connection.  However this is done, the security capabilities
              provided by (D)TLS MUST be at least as high as the level of
              security indicated by the tmRequestedSecurityLevel parameter.
              The actual security level of the session is reported in the
              tmStateReference cache as tmSecurityLevel.  For (D)TLS to provide
              strong authentication, each principal acting as a command
              generator SHOULD have its own certificate.
        */
        /*
          Implementation notes: we do most of this in the
          sslctx_client_setup The transport should have been
          f_config()ed with the proper fingerprints to use (which is
          stored in tlsdata), or we'll use the default identity
          fingerprint if that can be found.
        */

        /* XXX: check securityLevel and ensure no NULL fingerprints are used */

        /* set up the needed SSL context */
        tlsdata->ssl_context = ctx =
                                   sslctx_client_setup(TLSv1_method(), tlsdata);
        if (!ctx) {
            snmp_log(LOG_ERR, "failed to create TLS context\n");
            return NULL;
        }

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
           3)  Using the destTransportDomain and destTransportAddress values,
               the client will initiate the (D)TLS handshake protocol to
               establish session keys for message integrity and encryption.
        */
        /* Implementation note:
           The transport domain and address are pre-processed by this point
        */

        /* Create a BIO connection for it */
        DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n",
                    tlsdata->addr_string));
        t->remote = (void *) strdup(tlsdata->addr_string);
        t->remote_length = strlen(tlsdata->addr_string) + 1;

        bio = BIO_new_connect(tlsdata->addr_string);

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
           3) continued:
              If the attempt to establish a session is unsuccessful, then
              snmpTlstmSessionOpenErrors is incremented, an error indication is
              returned, and processing stops.
        */

        if (NULL == bio) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create bio\n");
            _openssl_log_error(rc, NULL, "BIO creation");
            return NULL;
        }


        /* Tell the BIO to actually do the connection */
        if ((rc = BIO_do_connect(bio)) <= 0) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n",
                     tlsdata->addr_string);
            _openssl_log_error(rc, NULL, "BIO_do_connect");
            BIO_free(bio);
            return NULL;
        }

        /* Create the SSL layer on top of the socket bio */
        ssl = tlsdata->ssl = SSL_new(ctx);
        if (NULL == ssl) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
            BIO_free(bio);
            return NULL;
        }

        /* Bind the SSL layer to the BIO */
        SSL_set_bio(ssl, bio, bio);
        SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

        verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info);
        if (NULL == verify_info) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
            SSL_shutdown(ssl);
            BIO_free(bio);
            return NULL;
        }

        SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info);

        /* Then have SSL do it's connection over the BIO */
        if ((rc = SSL_connect(ssl)) <= 0) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n");
            BIO_free(bio);
            return NULL;
        }

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           3) continued:
              If the session failed to open because the presented
              server certificate was unknown or invalid then the
              snmpTlstmSessionUnknownServerCertificate or
              snmpTlstmSessionInvalidServerCertificates MUST be
              incremented and a snmpTlstmServerCertificateUnknown or
              snmpTlstmServerInvalidCertificate notification SHOULD be
              sent as appropriate.  Reasons for server certificate
              invalidation includes, but is not limited to,
              cryptographic validation failures and an unexpected
              presented certificate identity.
        */

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           4)  The (D)TLS client MUST then verify that the (D)TLS server's
               presented certificate is the expected certificate.  The (D)TLS
               client MUST NOT transmit SNMP messages until the server
               certificate has been authenticated, the client certificate has
               been transmitted and the TLS connection has been fully
               established.

               If the connection is being established from configuration based
               on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable
               DESCRIPTION clause describes how the verification is done (using
               either a certificate fingerprint, or an identity authenticated
               via certification path validation).

               If the connection is being established for reasons other than
               configuration found in the SNMP-TARGET-MIB then configuration and
               procedures outside the scope of this document should be followed.
               Configuration mechanisms SHOULD be similar in nature to those
               defined in the snmpTlstmAddrTable to ensure consistency across
               management configuration systems.  For example, a command-line
               tool for generating SNMP GETs might support specifying either the
               server's certificate fingerprint or the expected host name as a
               command line argument.
        */

        /* Implementation notes:
           - All remote certificate fingerprints are expected to be
             stored in the transport's config information.  This is
             true both for CLI clients and TARGET-MIB sessions.
           - netsnmp_tlsbase_verify_server_cert implements these checks
        */
        if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) {
            /* XXX: unknown vs invalid; two counters */
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE);
            snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n");
            SSL_shutdown(ssl);
            BIO_free(bio);
            return NULL;
        }

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           5)  (D)TLS provides assurance that the authenticated identity has
               been signed by a trusted configured certification authority.  If
               verification of the server's certificate fails in any way (for
               example because of failures in cryptographic verification or the
               presented identity did not match the expected named entity) then
               the session establishment MUST fail, the
               snmpTlstmSessionInvalidServerCertificates object is incremented.
               If the session can not be opened for any reason at all, including
               cryptographic verification failures, then the
               snmpTlstmSessionOpenErrors counter is incremented and processing
               stops.

        */
        /* XXX: add snmpTlstmSessionInvalidServerCertificates on
           crypto failure */

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           6)  The TLSTM-specific session identifier (tlstmSessionID) is set in
           the tmSessionID of the tmStateReference passed to the TLS
           Transport Model to indicate that the session has been established
           successfully and to point to a specific (D)TLS connection for
           future use.  The tlstmSessionID is also stored in the LCD for
           later lookup during processing of incoming messages
           (Section 5.1.2).
        */
        /* Implementation notes:
           - the tlsdata pointer is used as our session identifier, as
             noted in the netsnmp_tlstcp_recv() function comments.
        */

        t->sock = BIO_get_fd(bio, NULL);

    } else {
#ifndef NETSNMP_NO_LISTEN_SUPPORT
        /* Is the server */

        /* Create the socket bio */
        DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n",
                    tlsdata->addr_string));
        tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string);
        t->local = (void *) strdup(tlsdata->addr_string);
        t->local_length = strlen(tlsdata->addr_string)+1;
        if (NULL == tlsdata->accept_bio) {
            snmp_log(LOG_ERR, "TLSTCP: Falied to create a accept BIO\n");
            return NULL;
        }

        /* openssl requires an initial accept to bind() the socket */
        if (BIO_do_accept(tlsdata->accept_bio) <= 0) {
            _openssl_log_error(rc, tlsdata->ssl, "BIO_do__accept");
            snmp_log(LOG_ERR, "TLSTCP: Falied to do first accept on the TLS accept BIO\n");
            return NULL;
        }

        /* create the OpenSSL TLS context */
        tlsdata->ssl_context =
            sslctx_server_setup(TLSv1_method());

        t->sock = BIO_get_fd(tlsdata->accept_bio, NULL);
        t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
#else /* NETSNMP_NO_LISTEN_SUPPORT */
        return NULL;
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
    }
    return t;
}