1 /* 2 * libjingle 3 * Copyright 2012 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/p2p/base/transportdescriptionfactory.h" 29 30 #include "talk/p2p/base/transportdescription.h" 31 #include "webrtc/base/helpers.h" 32 #include "webrtc/base/logging.h" 33 #include "webrtc/base/messagedigest.h" 34 #include "webrtc/base/scoped_ptr.h" 35 #include "webrtc/base/sslfingerprint.h" 36 37 namespace cricket { 38 39 static TransportProtocol kDefaultProtocol = ICEPROTO_GOOGLE; 40 41 TransportDescriptionFactory::TransportDescriptionFactory() 42 : protocol_(kDefaultProtocol), 43 secure_(SEC_DISABLED), 44 identity_(NULL) { 45 } 46 47 TransportDescription* TransportDescriptionFactory::CreateOffer( 48 const TransportOptions& options, 49 const TransportDescription* current_description) const { 50 rtc::scoped_ptr<TransportDescription> desc(new TransportDescription()); 51 52 // Set the transport type depending on the selected protocol. 53 if (protocol_ == ICEPROTO_RFC5245) { 54 desc->transport_type = NS_JINGLE_ICE_UDP; 55 } else if (protocol_ == ICEPROTO_HYBRID) { 56 desc->transport_type = NS_JINGLE_ICE_UDP; 57 desc->AddOption(ICE_OPTION_GICE); 58 } else if (protocol_ == ICEPROTO_GOOGLE) { 59 desc->transport_type = NS_GINGLE_P2P; 60 } 61 62 // Generate the ICE credentials if we don't already have them. 63 if (!current_description || options.ice_restart) { 64 desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH); 65 desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH); 66 } else { 67 desc->ice_ufrag = current_description->ice_ufrag; 68 desc->ice_pwd = current_description->ice_pwd; 69 } 70 71 // If we are trying to establish a secure transport, add a fingerprint. 72 if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) { 73 // Fail if we can't create the fingerprint. 74 // If we are the initiator set role to "actpass". 75 if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) { 76 return NULL; 77 } 78 } 79 80 return desc.release(); 81 } 82 83 TransportDescription* TransportDescriptionFactory::CreateAnswer( 84 const TransportDescription* offer, 85 const TransportOptions& options, 86 const TransportDescription* current_description) const { 87 // A NULL offer is treated as a GICE transport description. 88 // TODO(juberti): Figure out why we get NULL offers, and fix this upstream. 89 rtc::scoped_ptr<TransportDescription> desc(new TransportDescription()); 90 91 // Figure out which ICE variant to negotiate; prefer RFC 5245 ICE, but fall 92 // back to G-ICE if needed. Note that we never create a hybrid answer, since 93 // we know what the other side can support already. 94 if (offer && offer->transport_type == NS_JINGLE_ICE_UDP && 95 (protocol_ == ICEPROTO_RFC5245 || protocol_ == ICEPROTO_HYBRID)) { 96 // Offer is ICE or hybrid, we support ICE or hybrid: use ICE. 97 desc->transport_type = NS_JINGLE_ICE_UDP; 98 } else if (offer && offer->transport_type == NS_JINGLE_ICE_UDP && 99 offer->HasOption(ICE_OPTION_GICE) && 100 protocol_ == ICEPROTO_GOOGLE) { 101 desc->transport_type = NS_GINGLE_P2P; 102 // Offer is hybrid, we support GICE: use GICE. 103 } else if ((!offer || offer->transport_type == NS_GINGLE_P2P) && 104 (protocol_ == ICEPROTO_HYBRID || protocol_ == ICEPROTO_GOOGLE)) { 105 // Offer is GICE, we support hybrid or GICE: use GICE. 106 desc->transport_type = NS_GINGLE_P2P; 107 } else { 108 // Mismatch. 109 LOG(LS_WARNING) << "Failed to create TransportDescription answer " 110 "because of incompatible transport types"; 111 return NULL; 112 } 113 114 // Generate the ICE credentials if we don't already have them or ice is 115 // being restarted. 116 if (!current_description || options.ice_restart) { 117 desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH); 118 desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH); 119 } else { 120 desc->ice_ufrag = current_description->ice_ufrag; 121 desc->ice_pwd = current_description->ice_pwd; 122 } 123 124 // Negotiate security params. 125 if (offer && offer->identity_fingerprint.get()) { 126 // The offer supports DTLS, so answer with DTLS, as long as we support it. 127 if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) { 128 // Fail if we can't create the fingerprint. 129 // Setting DTLS role to active. 130 ConnectionRole role = (options.prefer_passive_role) ? 131 CONNECTIONROLE_PASSIVE : CONNECTIONROLE_ACTIVE; 132 133 if (!SetSecurityInfo(desc.get(), role)) { 134 return NULL; 135 } 136 } 137 } else if (secure_ == SEC_REQUIRED) { 138 // We require DTLS, but the other side didn't offer it. Fail. 139 LOG(LS_WARNING) << "Failed to create TransportDescription answer " 140 "because of incompatible security settings"; 141 return NULL; 142 } 143 144 return desc.release(); 145 } 146 147 bool TransportDescriptionFactory::SetSecurityInfo( 148 TransportDescription* desc, ConnectionRole role) const { 149 if (!identity_) { 150 LOG(LS_ERROR) << "Cannot create identity digest with no identity"; 151 return false; 152 } 153 154 // This digest algorithm is used to produce the a=fingerprint lines in SDP. 155 // RFC 4572 Section 5 requires that those lines use the same hash function as 156 // the certificate's signature. 157 std::string digest_alg; 158 if (!identity_->certificate().GetSignatureDigestAlgorithm(&digest_alg)) { 159 LOG(LS_ERROR) << "Failed to retrieve the certificate's digest algorithm"; 160 return false; 161 } 162 163 desc->identity_fingerprint.reset( 164 rtc::SSLFingerprint::Create(digest_alg, identity_)); 165 if (!desc->identity_fingerprint.get()) { 166 LOG(LS_ERROR) << "Failed to create identity fingerprint, alg=" 167 << digest_alg; 168 return false; 169 } 170 171 // Assign security role. 172 desc->connection_role = role; 173 return true; 174 } 175 176 } // namespace cricket 177 178