From 95545ba14b27fdfd505c5c7f9f66660dba68008b Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Wed, 11 Jan 2017 01:41:31 +0000 Subject: Fixed #1960: Export SIP transport TLS state and TLS certificate info to PJSUA2 git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5518 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip-apps/src/swig/symbols.i | 4 + pjsip-apps/src/swig/symbols.lst | 2 +- pjsip/include/pjsua2/endpoint.hpp | 160 ++++++++++++++++++++++++++++++++++++++ pjsip/src/pjsua2/endpoint.cpp | 93 ++++++++++++++++++++++ 4 files changed, 258 insertions(+), 1 deletion(-) diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i index 03bfa89f..5edacf2b 100644 --- a/pjsip-apps/src/swig/symbols.i +++ b/pjsip-apps/src/swig/symbols.i @@ -34,6 +34,10 @@ typedef enum pj_ssl_cipher {PJ_TLS_UNKNOWN_CIPHER = -1, PJ_TLS_NULL_WITH_NULL_NU typedef enum pj_ssl_sock_proto {PJ_SSL_SOCK_PROTO_DEFAULT = 0, PJ_SSL_SOCK_PROTO_SSL2 = 1 << 0, PJ_SSL_SOCK_PROTO_SSL3 = 1 << 1, PJ_SSL_SOCK_PROTO_TLS1 = 1 << 2, PJ_SSL_SOCK_PROTO_TLS1_1 = 1 << 3, PJ_SSL_SOCK_PROTO_TLS1_2 = 1 << 4, PJ_SSL_SOCK_PROTO_SSL23 = (1 << 16) - 1, PJ_SSL_SOCK_PROTO_DTLS1 = 1 << 16} pj_ssl_sock_proto; +typedef enum pj_ssl_cert_name_type {PJ_SSL_CERT_NAME_UNKNOWN = 0, PJ_SSL_CERT_NAME_RFC822, PJ_SSL_CERT_NAME_DNS, PJ_SSL_CERT_NAME_URI, PJ_SSL_CERT_NAME_IP} pj_ssl_cert_name_type; + +typedef enum pj_ssl_cert_verify_flag_t {PJ_SSL_CERT_ESUCCESS = 0, PJ_SSL_CERT_EISSUER_NOT_FOUND = (1 << 0), PJ_SSL_CERT_EUNTRUSTED = (1 << 1), PJ_SSL_CERT_EVALIDITY_PERIOD = (1 << 2), PJ_SSL_CERT_EINVALID_FORMAT = (1 << 3), PJ_SSL_CERT_EINVALID_PURPOSE = (1 << 4), PJ_SSL_CERT_EISSUER_MISMATCH = (1 << 5), PJ_SSL_CERT_ECRL_FAILURE = (1 << 6), PJ_SSL_CERT_EREVOKED = (1 << 7), PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8), PJ_SSL_CERT_EIDENTITY_NOT_MATCH = (1 << 30), PJ_SSL_CERT_EUNKNOWN = (1 << 31)} pj_ssl_cert_verify_flag_t; + typedef enum pj_stun_nat_type {PJ_STUN_NAT_TYPE_UNKNOWN, PJ_STUN_NAT_TYPE_ERR_UNKNOWN, PJ_STUN_NAT_TYPE_OPEN, PJ_STUN_NAT_TYPE_BLOCKED, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP, PJ_STUN_NAT_TYPE_FULL_CONE, PJ_STUN_NAT_TYPE_SYMMETRIC, PJ_STUN_NAT_TYPE_RESTRICTED, PJ_STUN_NAT_TYPE_PORT_RESTRICTED} pj_stun_nat_type; typedef enum pj_turn_tp_type {PJ_TURN_TP_UDP = 17, PJ_TURN_TP_TCP = 6, PJ_TURN_TP_TLS = 255} pj_turn_tp_type; diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst index 35bed882..651e78d4 100644 --- a/pjsip-apps/src/swig/symbols.lst +++ b/pjsip-apps/src/swig/symbols.lst @@ -2,7 +2,7 @@ pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint pj/file_io.h pj_file_access pj/log.h pj_log_decoration pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params -pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto +pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pjnath/nat_detect.h pj_stun_nat_type pjnath/turn_session.h pj_turn_tp_type diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index 4fdd0405..da777868 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -123,6 +123,140 @@ struct OnTimerParam unsigned msecDelay; }; +/** + * SSL certificate type and name structure. + */ +struct SslCertName +{ + pj_ssl_cert_name_type type; /**< Name type */ + string name; /**< The name */ +}; + +/** + * SSL certificate information. + */ +struct SslCertInfo +{ + unsigned version; /**< Certificate version */ + unsigned char serialNo[20]; /**< Serial number, array + of octets, first index + is MSB */ + string subjectCn; /**< Subject common name */ + string subjectInfo; /**< One line subject, fields + are separated by slash, e.g: + "CN=sample.org/OU=HRD" */ + + string issuerCn; /**< Issuer common name */ + string issuerInfo; /**< One line subject, fields + are separated by slash */ + + TimeVal validityStart; /**< Validity start */ + TimeVal validityEnd; /**< Validity end */ + bool validityGmt; /**< Flag if validity + date/time use GMT */ + + vector subjectAltName; /**< Subject alternative + name extension */ + + string raw; /**< Raw certificate in PEM + format, only available + for remote certificate */ + +public: + /** + * Check if the info is set with empty values. + * + * @return True if the info is empty. + */ + bool isEmpty() const; + + /** + * Convert from pjsip + */ + void fromPj(const pj_ssl_cert_info &info); +}; + +/** + * TLS transport information. + */ +struct TlsInfo +{ + /** + * Describes whether secure socket connection is established, i.e: TLS/SSL + * handshaking has been done successfully. + */ + bool established; + + /** + * Describes secure socket protocol being used, see #pj_ssl_sock_proto. + * Use bitwise OR operation to combine the protocol type. + */ + unsigned protocol; + + /** + * Describes cipher suite being used, this will only be set when connection + * is established. + */ + pj_ssl_cipher cipher; + + /** + * Describes cipher name being used, this will only be set when connection + * is established. + */ + string cipherName; + + /** + * Describes local address. + */ + SocketAddress localAddr; + + /** + * Describes remote address. + */ + SocketAddress remoteAddr; + + /** + * Describes active local certificate info. Use SslCertInfo.isEmpty() + * to check if the local cert info is available. + */ + SslCertInfo localCertInfo; + + /** + * Describes active remote certificate info. Use SslCertInfo.isEmpty() + * to check if the remote cert info is available. + */ + SslCertInfo remoteCertInfo; + + /** + * Status of peer certificate verification. + */ + unsigned verifyStatus; + + /** + * Error messages (if any) of peer certificate verification, based on + * the field verifyStatus above. + */ + StringVector verifyMsgs; + +public: + /** + * Constructor. + */ + TlsInfo(); + + /** + * Check if the info is set with empty values. + * + * @return True if the info is empty. + */ + bool isEmpty() const; + + /** + * Convert from pjsip + */ + void fromPj(const pjsip_tls_state_info &info); +}; + /** * Parameter of Endpoint::onTransportState() callback. */ @@ -132,6 +266,11 @@ struct OnTransportStateParam * The transport handle. */ TransportHandle hnd; + + /** + * The transport type. + */ + string type; /** * Transport current state. @@ -142,6 +281,12 @@ struct OnTransportStateParam * The last error code related to the transport state. */ pj_status_t lastError; + + /** + * TLS transport info, only used if transport type is TLS. Use + * TlsInfo.isEmpty() to check if this info is available. + */ + TlsInfo tlsInfo; }; /** @@ -1029,6 +1174,21 @@ public: * @param id Transport ID. */ void transportClose(TransportId id) throw(Error); + + /** + * Start graceful shutdown procedure for this transport handle. After + * graceful shutdown has been initiated, no new reference can be + * obtained for the transport. However, existing objects that currently + * uses the transport may still use this transport to send and receive + * packets. After all objects release their reference to this transport, + * the transport will be destroyed immediately. + * + * Note: application normally uses this API after obtaining the handle + * from onTransportState() callback. + * + * @param tp The transport. + */ + void transportShutdown(TransportHandle tp) throw(Error); /************************************************************************* * Call operations diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index a66cfcf1..163e292f 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -44,6 +44,82 @@ Endpoint *Endpoint::instance_; /////////////////////////////////////////////////////////////////////////////// +TlsInfo::TlsInfo() +{ + pj_bzero(this, sizeof(TlsInfo)); +} + +bool TlsInfo::isEmpty() const +{ + TlsInfo dummy; + + pj_bzero(&dummy, sizeof(dummy)); + return ((pj_memcmp(this, &dummy, sizeof(dummy)) == 0)? true: false); +} + +void TlsInfo::fromPj(const pjsip_tls_state_info &info) +{ +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 + pj_ssl_sock_info *ssock_info = info.ssl_sock_info; + char straddr[PJ_INET6_ADDRSTRLEN+10]; + const char *verif_msgs[32]; + unsigned verif_msg_cnt; + + established = PJ2BOOL(ssock_info->established); + protocol = ssock_info->proto; + cipher = ssock_info->cipher; + cipherName = pj_ssl_cipher_name(ssock_info->cipher); + pj_sockaddr_print(&ssock_info->local_addr, straddr, sizeof(straddr), 3); + localAddr = straddr; + pj_sockaddr_print(&ssock_info->remote_addr, straddr, sizeof(straddr),3); + remoteAddr = straddr; + verifyStatus = ssock_info->verify_status; + if (ssock_info->local_cert_info) + localCertInfo.fromPj(*ssock_info->local_cert_info); + if (ssock_info->remote_cert_info) + remoteCertInfo.fromPj(*ssock_info->remote_cert_info); + + /* Dump server TLS certificate verification result */ + verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs); + pj_ssl_cert_get_verify_status_strings(ssock_info->verify_status, + verif_msgs, &verif_msg_cnt); + for (unsigned i = 0; i < verif_msg_cnt; ++i) { + verifyMsgs.push_back(verif_msgs[i]); + } +#endif +} + +bool SslCertInfo::isEmpty() const +{ + SslCertInfo dummy; + + pj_bzero(&dummy, sizeof(dummy)); + return ((pj_memcmp(this, &dummy, sizeof(dummy)) == 0)? true: false); +} + +void SslCertInfo::fromPj(const pj_ssl_cert_info &info) +{ + version = info.version; + pj_memcpy(serialNo, info.serial_no, sizeof(info.serial_no)); + subjectCn = pj2Str(info.subject.cn); + subjectInfo = pj2Str(info.subject.info); + issuerCn = pj2Str(info.issuer.cn); + issuerInfo = pj2Str(info.issuer.info); + validityStart.fromPj(info.validity.start); + validityEnd.fromPj(info.validity.end); + validityGmt = PJ2BOOL(info.validity.gmt); + raw = pj2Str(info.raw); + + for (unsigned i = 0; i < info.subj_alt_name.cnt; i++) { + SslCertName cname; + cname.type = info.subj_alt_name.entry[i].type; + cname.name = pj2Str(info.subj_alt_name.entry[i].name); + subjectAltName.push_back(cname); + } +} + +/////////////////////////////////////////////////////////////////////////////// + UaConfig::UaConfig() : mainThreadOnly(false) { @@ -558,8 +634,20 @@ void Endpoint::on_transport_state( pjsip_transport *tp, OnTransportStateParam prm; prm.hnd = (TransportHandle)tp; + prm.type = tp->type_name; prm.state = state; prm.lastError = info ? info->status : PJ_SUCCESS; + pj_bzero(&prm.tlsInfo, sizeof(TlsInfo)); + +#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 + if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info && + (state == PJSIP_TP_STATE_CONNECTED || + ((pjsip_tls_state_info*)info->ext_info)-> + ssl_sock_info->verify_status != PJ_SUCCESS)) + { + prm.tlsInfo.fromPj(*((pjsip_tls_state_info*)info->ext_info)); + } +#endif ep.onTransportState(prm); } @@ -1694,6 +1782,11 @@ void Endpoint::transportClose(TransportId id) throw(Error) PJSUA2_CHECK_EXPR( pjsua_transport_close(id, PJ_FALSE) ); } +void Endpoint::transportShutdown(TransportHandle tp) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsip_transport_shutdown((pjsip_transport *)tp) ); +} + /////////////////////////////////////////////////////////////////////////////// /* * Call operations -- cgit v1.2.3