1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <IceSSL/SChannelEngine.h>
6 #include <IceSSL/SChannelTransceiverI.h>
7 #include <IceSSL/Plugin.h>
8 #include <IceSSL/Util.h>
9 
10 #include <Ice/LocalException.h>
11 #include <Ice/Logger.h>
12 #include <Ice/Communicator.h>
13 #include <Ice/StringConverter.h>
14 
15 #include <IceUtil/StringUtil.h>
16 #include <IceUtil/FileUtil.h>
17 #include <IceUtil/MutexPtrLock.h>
18 #include <Ice/UUID.h>
19 
20 #include <wincrypt.h>
21 
22 //
23 // SP_PROT_TLS1_3 is new in v10.0.15021 SDK
24 //
25 #ifndef SP_PROT_TLS1_3
26 #    define SP_PROT_TLS1_3_SERVER 0x00001000
27 #    define SP_PROT_TLS1_3_CLIENT 0x00002000
28 #    define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_SERVER | SP_PROT_TLS1_3_CLIENT)
29 #endif
30 
31 //
32 // CALG_ECDH_EPHEM algorithm constant is not defined in older version of the SDK headers
33 //
34 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
35 //
36 
37 const int ICESSL_CALG_ECDH_EPHEM = 0x0000AE06;
38 
39 //
40 // COMPILERFIX SCH_USE_STRONG_CRYPTO not defined with v90
41 //
42 #if defined(_MSC_VER) && (_MSC_VER <= 1600)
43 #  ifndef SCH_USE_STRONG_CRYPTO
44 #    define SCH_USE_STRONG_CRYPTO 0x00400000
45 #  endif
46 #endif
47 
48 using namespace std;
49 using namespace Ice;
50 using namespace IceUtil;
51 using namespace IceUtilInternal;
52 using namespace IceSSL;
53 
upCast(SChannel::SSLEngine * p)54 Shared* SChannel::upCast(SChannel::SSLEngine* p)
55 {
56     return p;
57 }
58 
59 namespace
60 {
61 
62 IceUtil::Mutex* globalMutex;
63 
64 class Init
65 {
66 public:
67 
Init()68     Init()
69     {
70         globalMutex = new IceUtil::Mutex;
71     }
72 
~Init()73     ~Init()
74     {
75         delete globalMutex;
76         globalMutex = 0;
77     }
78 };
79 
80 Init init;
81 
82 void
addMatchingCertificates(HCERTSTORE source,HCERTSTORE target,DWORD findType,const void * findParam)83 addMatchingCertificates(HCERTSTORE source, HCERTSTORE target, DWORD findType, const void* findParam)
84 {
85     PCCERT_CONTEXT next = 0;
86     do
87     {
88         if((next = CertFindCertificateInStore(source, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
89                                               findType, findParam, next)) != 0)
90         {
91             if(!CertAddCertificateContextToStore(target, next, CERT_STORE_ADD_ALWAYS, 0))
92             {
93                 throw PluginInitializationException(__FILE__, __LINE__,
94                     "IceSSL: error adding certificate to store:\n" + IceUtilInternal::lastErrorToString());
95             }
96         }
97     }
98     while(next);
99 }
100 
101 vector<PCCERT_CONTEXT>
findCertificates(const string & location,const string & storeName,const string & value,vector<HCERTSTORE> & stores)102 findCertificates(const string& location, const string& storeName, const string& value, vector<HCERTSTORE>& stores)
103 {
104     DWORD storeLoc;
105     if(location == "CurrentUser")
106     {
107         storeLoc = CERT_SYSTEM_STORE_CURRENT_USER;
108     }
109     else
110     {
111         storeLoc = CERT_SYSTEM_STORE_LOCAL_MACHINE;
112     }
113 
114     HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, storeLoc, Ice::stringToWstring(storeName).c_str());
115     if(!store)
116     {
117         throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: failed to open certificate store `" + storeName +
118                                             "':\n" + IceUtilInternal::lastErrorToString());
119     }
120 
121     //
122     // Start with all of the certificates in the collection and filter as necessary.
123     //
124     // - If the value is "*", return all certificates.
125     // - Otherwise, search using key:value pairs. The following keys are supported:
126     //
127     //   Issuer
128     //   IssuerDN
129     //   Serial
130     //   Subject
131     //   SubjectDN
132     //   SubjectKeyId
133     //   Thumbprint
134     //
135     //   A value must be enclosed in single or double quotes if it contains whitespace.
136     //
137     HCERTSTORE tmpStore = 0;
138     try
139     {
140         if(value != "*")
141         {
142             if(value.find(':', 0) == string::npos)
143             {
144                 throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: no key in `" + value + "'");
145             }
146             size_t start = 0;
147             size_t pos;
148             while((pos = value.find(':', start)) != string::npos)
149             {
150                 string field = IceUtilInternal::toUpper(IceUtilInternal::trim(value.substr(start, pos - start)));
151                 if(field != "SUBJECT" && field != "SUBJECTDN" && field != "ISSUER" && field != "ISSUERDN" &&
152                    field != "THUMBPRINT" && field != "SUBJECTKEYID" && field != "SERIAL")
153                 {
154                     throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unknown key in `" + value + "'");
155                 }
156 
157                 start = pos + 1;
158                 while(start < value.size() && (value[start] == ' ' || value[start] == '\t'))
159                 {
160                     ++start;
161                 }
162 
163                 if(start == value.size())
164                 {
165                     throw PluginInitializationException(__FILE__, __LINE__,
166                                                         "IceSSL: missing argument in `" + value + "'");
167                 }
168 
169                 string arg;
170                 if(value[start] == '"' || value[start] == '\'')
171                 {
172                     size_t end = start;
173                     ++end;
174                     while(end < value.size())
175                     {
176                         if(value[end] == value[start] && value[end - 1] != '\\')
177                         {
178                             break;
179                         }
180                         ++end;
181                     }
182                     if(end == value.size() || value[end] != value[start])
183                     {
184                         throw PluginInitializationException(__FILE__, __LINE__,
185                                                             "IceSSL: unmatched quote in `" + value + "'");
186                     }
187                     ++start;
188                     arg = value.substr(start, end - start);
189                     start = end + 1;
190                 }
191                 else
192                 {
193                     size_t end = value.find_first_of(" \t", start);
194                     if(end == string::npos)
195                     {
196                         arg = value.substr(start);
197                         start = value.size();
198                     }
199                     else
200                     {
201                         arg = value.substr(start, end - start);
202                         start = end + 1;
203                     }
204                 }
205 
206                 tmpStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0);
207                 if(!tmpStore)
208                 {
209                     throw PluginInitializationException(__FILE__, __LINE__,
210                                 "IceSSL: error adding certificate to store:\n" + IceUtilInternal::lastErrorToString());
211                 }
212 
213                 if(field == "SUBJECT" || field == "ISSUER")
214                 {
215                     const wstring argW = Ice::stringToWstring(arg);
216                     DWORD findType = field == "SUBJECT" ? CERT_FIND_SUBJECT_STR : CERT_FIND_ISSUER_STR;
217                     addMatchingCertificates(store, tmpStore, findType, argW.c_str());
218                 }
219                 else if(field == "SUBJECTDN" || field == "ISSUERDN")
220                 {
221                     const wstring argW = Ice::stringToWstring(arg);
222                     DWORD flags[] = {
223                         CERT_OID_NAME_STR,
224                         CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
225                         CERT_OID_NAME_STR | CERT_NAME_STR_FORCE_UTF8_DIR_STR_FLAG,
226                         CERT_OID_NAME_STR | CERT_NAME_STR_FORCE_UTF8_DIR_STR_FLAG | CERT_NAME_STR_REVERSE_FLAG
227                     };
228                     for(size_t i = 0; i < sizeof(flags) / sizeof(DWORD); ++i)
229                     {
230                         DWORD length = 0;
231                         if(!CertStrToNameW(X509_ASN_ENCODING, argW.c_str(), flags[i], 0, 0, &length, 0))
232                         {
233                             throw PluginInitializationException(
234                                 __FILE__, __LINE__,
235                                 "IceSSL: invalid value `" + value + "' for `IceSSL.FindCert' property:\n" +
236                                 IceUtilInternal::lastErrorToString());
237                         }
238 
239                         vector<BYTE> buffer(length);
240                         if(!CertStrToNameW(X509_ASN_ENCODING, argW.c_str(), flags[i], 0, &buffer[0], &length, 0))
241                         {
242                             throw PluginInitializationException(
243                                 __FILE__, __LINE__,
244                                 "IceSSL: invalid value `" + value + "' for `IceSSL.FindCert' property:\n" +
245                                 IceUtilInternal::lastErrorToString());
246                         }
247 
248                         CERT_NAME_BLOB name = { length, &buffer[0] };
249 
250                         DWORD findType = field == "SUBJECTDN" ? CERT_FIND_SUBJECT_NAME : CERT_FIND_ISSUER_NAME;
251                         addMatchingCertificates(store, tmpStore, findType, &name);
252                     }
253                 }
254                 else if(field == "THUMBPRINT" || field == "SUBJECTKEYID")
255                 {
256                     vector<BYTE> buffer;
257                     if(!parseBytes(arg, buffer))
258                     {
259                         throw PluginInitializationException(__FILE__, __LINE__,
260                                                 "IceSSL: invalid `IceSSL.FindCert' property: can't decode the value");
261                     }
262 
263                     CRYPT_HASH_BLOB hash = { static_cast<DWORD>(buffer.size()), &buffer[0] };
264                     DWORD findType = field == "THUMBPRINT" ? CERT_FIND_HASH : CERT_FIND_KEY_IDENTIFIER;
265                     addMatchingCertificates(store, tmpStore, findType, &hash);
266                 }
267                 else if(field == "SERIAL")
268                 {
269                     vector<BYTE> buffer;
270                     if(!parseBytes(arg, buffer))
271                     {
272                         throw PluginInitializationException(__FILE__, __LINE__,
273                                                 "IceSSL: invalid value `" + value + "' for `IceSSL.FindCert' property");
274                     }
275 
276                     CRYPT_INTEGER_BLOB serial = { static_cast<DWORD>(buffer.size()), &buffer[0] };
277                     PCCERT_CONTEXT next = 0;
278                     do
279                     {
280                         if((next = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
281                                                               CERT_FIND_ANY, 0, next)) != 0)
282                         {
283                             if(CertCompareIntegerBlob(&serial, &next->pCertInfo->SerialNumber))
284                             {
285                                 if(!CertAddCertificateContextToStore(tmpStore, next, CERT_STORE_ADD_ALWAYS, 0))
286                                 {
287                                     throw PluginInitializationException(__FILE__, __LINE__,
288                                                                     "IceSSL: error adding certificate to store:\n" +
289                                                                     IceUtilInternal::lastErrorToString());
290                                 }
291                             }
292                         }
293                     }
294                     while(next);
295                 }
296                 CertCloseStore(store, 0);
297                 store = tmpStore;
298             }
299         }
300     }
301     catch(...)
302     {
303         if(store && store != tmpStore)
304         {
305             CertCloseStore(store, 0);
306         }
307 
308         if(tmpStore)
309         {
310             CertCloseStore(tmpStore, 0);
311             tmpStore = 0;
312         }
313         throw;
314     }
315 
316     vector<PCCERT_CONTEXT> certs;
317     if(store)
318     {
319         PCCERT_CONTEXT next = 0;
320         do
321         {
322             if((next = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, 0,
323                                                   next)) != 0)
324             {
325                 certs.push_back(next);
326             }
327         }
328         while(next);
329         stores.push_back(store);
330     }
331     return certs;
332 }
333 
334 #if defined(__MINGW32__) || (defined(_MSC_VER) && (_MSC_VER <= 1500))
335 //
336 // CERT_CHAIN_ENGINE_CONFIG struct in mingw headers doesn't include
337 // new members added in Windows 7, we add our ouwn definition and
338 // then cast it to CERT_CHAIN_ENGINE_CONFIG this works because the
339 // linked libraries include the new version.
340 //
341 struct CertChainEngineConfig
342 {
343     DWORD cbSize;
344     HCERTSTORE hRestrictedRoot;
345     HCERTSTORE hRestrictedTrust;
346     HCERTSTORE hRestrictedOther;
347     DWORD cAdditionalStore;
348     HCERTSTORE *rghAdditionalStore;
349     DWORD dwFlags;
350     DWORD dwUrlRetrievalTimeout;
351     DWORD MaximumCachedCertificates;
352     DWORD CycleDetectionModulus;
353     HCERTSTORE hExclusiveRoot;
354     HCERTSTORE hExclusiveTrustedPeople;
355 };
356 
357 #endif
358 
359 void
addCertificatesToStore(const string & file,HCERTSTORE store,PCCERT_CONTEXT * cert=0)360 addCertificatesToStore(const string& file, HCERTSTORE store, PCCERT_CONTEXT* cert = 0)
361 {
362     vector<char> buffer;
363     readFile(file, buffer);
364     if(buffer.empty())
365     {
366         throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate file is empty:\n" + file);
367     }
368 
369     string strbuf(buffer.begin(), buffer.end());
370     string::size_type size, startpos, endpos = 0;
371     bool first = true;
372     while(true)
373     {
374         startpos = strbuf.find("-----BEGIN CERTIFICATE-----", endpos);
375         if(startpos != string::npos)
376         {
377             endpos = strbuf.find("-----END CERTIFICATE-----", startpos);
378             size = endpos - startpos + sizeof("-----END CERTIFICATE-----");
379         }
380         else if(first)
381         {
382             startpos = 0;
383             endpos = string::npos;
384             size = strbuf.size();
385         }
386         else
387         {
388             break;
389         }
390 
391         vector<BYTE> outBuffer;
392         outBuffer.resize(size);
393         DWORD outLength = static_cast<DWORD>(outBuffer.size());
394         if(!CryptStringToBinary(&buffer[startpos], static_cast<DWORD>(size), CRYPT_STRING_ANY, &outBuffer[0],
395                                 &outLength, 0, 0))
396         {
397             assert(GetLastError() != ERROR_MORE_DATA); // Base64 data should always be bigger than binary
398             throw PluginInitializationException(__FILE__, __LINE__,
399                                                 "IceSSL: error decoding certificate:\n" + lastErrorToString());
400         }
401 
402         if(!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &outBuffer[0],
403                                              outLength, CERT_STORE_ADD_NEW, first ? cert : 0))
404         {
405             if(GetLastError() != static_cast<DWORD>(CRYPT_E_EXISTS))
406             {
407                 throw PluginInitializationException(__FILE__, __LINE__,
408                                                     "IceSSL: error decoding certificate:\n" + lastErrorToString());
409             }
410         }
411 
412         first = false;
413     }
414 }
415 
416 DWORD
parseProtocols(const StringSeq & protocols)417 parseProtocols(const StringSeq& protocols)
418 {
419     DWORD v = 0;
420 
421     for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
422     {
423         string prot = IceUtilInternal::toUpper(*p);
424 
425         if(prot == "SSL3" || prot == "SSLV3")
426         {
427             v |= SP_PROT_SSL3;
428         }
429         else if(prot == "TLS" || prot == "TLS1" || prot == "TLSV1" || prot == "TLS1_0" || prot == "TLSV1_0")
430         {
431             v |= SP_PROT_TLS1;
432         }
433         else if(prot == "TLS1_1" || prot == "TLSV1_1")
434         {
435             v |= SP_PROT_TLS1_1;
436         }
437         else if(prot == "TLS1_2" || prot == "TLSV1_2")
438         {
439             v |= SP_PROT_TLS1_2;
440         }
441         else if (prot == "TLS1_3" || prot == "TLSV1_2")
442         {
443             v |= SP_PROT_TLS1_3;
444         }
445         else
446         {
447             throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unrecognized protocol `" + *p + "'");
448         }
449     }
450 
451     return v;
452 }
453 
454 const ALG_ID supportedCiphers[] = { CALG_3DES, CALG_AES_128, CALG_AES_256, CALG_DES, CALG_RC2, CALG_RC4 };
455 const int supportedCiphersSize = sizeof(supportedCiphers)/sizeof(ALG_ID);
456 
457 ALG_ID
algorithmId(const string & name)458 algorithmId(const string& name)
459 {
460     if(name == "3DES")
461     {
462         return CALG_3DES;
463     }
464     else if(name == "3DES_112")
465     {
466         return CALG_3DES_112;
467     }
468     else if(name == "AES")
469     {
470         return CALG_AES;
471     }
472     else if(name == "AES_128")
473     {
474         return CALG_AES_128;
475     }
476     else if(name == "AES_192")
477     {
478         return CALG_AES_192;
479     }
480     else if(name == "AES_256")
481     {
482         return CALG_AES_256;
483     }
484     else if(name == "AGREEDKEY_ANY")
485     {
486         return CALG_AGREEDKEY_ANY;
487     }
488     else if(name == "CYLINK_MEK")
489     {
490         return CALG_CYLINK_MEK;
491     }
492     else if(name == "DES")
493     {
494         return CALG_DES;
495     }
496     else if(name == "DESX")
497     {
498         return CALG_DESX;
499     }
500     else if(name == "DH_EPHEM")
501     {
502         return CALG_DH_EPHEM;
503     }
504     else if(name == "DH_SF")
505     {
506         return CALG_DH_SF;
507     }
508     else if(name == "DSS_SIGN")
509     {
510         return CALG_DSS_SIGN;
511     }
512     else if(name == "ECDH")
513     {
514         return CALG_ECDH;
515     }
516     else if(name == "ECDH_EPHEM")
517     {
518         return ICESSL_CALG_ECDH_EPHEM;
519     }
520     else if(name == "ECDSA")
521     {
522         return CALG_ECDSA;
523     }
524     else if(name == "HASH_REPLACE_OWF")
525     {
526         return CALG_HASH_REPLACE_OWF;
527     }
528     else if(name == "HUGHES_MD5")
529     {
530         return CALG_HUGHES_MD5;
531     }
532     else if(name == "HMAC")
533     {
534         return CALG_HMAC;
535     }
536     else if(name == "MAC")
537     {
538         return CALG_MAC;
539     }
540     else if(name == "MD2")
541     {
542         return CALG_MD2;
543     }
544     else if(name == "MD4")
545     {
546         return CALG_MD4;
547     }
548     else if(name == "MD5")
549     {
550         return CALG_MD5;
551     }
552     else if(name == "NO_SIGN")
553     {
554         return CALG_NO_SIGN;
555     }
556     else if(name == "RC2")
557     {
558         return CALG_RC2;
559     }
560     else if(name == "RC4")
561     {
562         return CALG_RC4;
563     }
564     else if(name == "RC5")
565     {
566         return CALG_RC5;
567     }
568     else if(name == "RSA_KEYX")
569     {
570         return CALG_RSA_KEYX;
571     }
572     else if(name == "RSA_SIGN")
573     {
574         return CALG_RSA_SIGN;
575     }
576     else if(name == "SHA1")
577     {
578         return CALG_SHA1;
579     }
580     else if(name == "SHA_256")
581     {
582         return CALG_SHA_256;
583     }
584     else if(name == "SHA_384")
585     {
586         return CALG_SHA_384;
587     }
588     else if(name == "SHA_512")
589     {
590         return CALG_SHA_512;
591     }
592     return 0;
593 }
594 
595 }
596 
SSLEngine(const CommunicatorPtr & communicator)597 SChannel::SSLEngine::SSLEngine(const CommunicatorPtr& communicator) :
598     IceSSL::SSLEngine(communicator),
599     _rootStore(0),
600     _chainEngine(0),
601     _strongCrypto(false)
602 {
603 }
604 
605 void
initialize()606 SChannel::SSLEngine::initialize()
607 {
608     //
609     // BUGFIX: we use a global mutex for the initialization of SChannel to
610     // avoid crashes ocurring with last SChannel updates see:
611     // https://github.com/zeroc-ice/ice/issues/242
612     //
613     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
614 
615     //
616     // We still have to acquire the instance mutex because it is used by the base
617     // class to access _initialized data member.
618     //
619     Mutex::Lock lock(_mutex);
620     if(_initialized)
621     {
622         return;
623     }
624 
625     IceSSL::SSLEngine::initialize();
626 
627     const string prefix = "IceSSL.";
628     const PropertiesPtr properties = communicator()->getProperties();
629 
630     //
631     // Protocols selects which protocols to enable, by default we only enable TLS1.0
632     // TLS1.1 and TLS1.2 to avoid security issues with SSLv3
633     //
634     vector<string> defaultProtocols;
635     defaultProtocols.push_back("tls1_0");
636     defaultProtocols.push_back("tls1_1");
637     defaultProtocols.push_back("tls1_2");
638     const_cast<DWORD&>(_protocols) =
639         parseProtocols(properties->getPropertyAsListWithDefault(prefix + "Protocols", defaultProtocols));
640 
641     const_cast<bool&>(_strongCrypto) = properties->getPropertyAsIntWithDefault(prefix + "SchannelStrongCrypto", 0) > 0;
642 
643     //
644     // Check for a default directory. We look in this directory for
645     // files mentioned in the configuration.
646     //
647     const string defaultDir = properties->getProperty(prefix + "DefaultDir");
648 
649     const int passwordRetryMax = properties->getPropertyAsIntWithDefault(prefix + "PasswordRetryMax", 3);
650     PasswordPromptPtr passwordPrompt = getPasswordPrompt();
651     setPassword(properties->getProperty(prefix + "Password"));
652 
653     string ciphers = properties->getProperty(prefix + "Ciphers");
654     if(!ciphers.empty())
655     {
656         parseCiphers(ciphers);
657     }
658 
659     if(securityTraceLevel() >= 1)
660     {
661         ostringstream os;
662         os << "enabling SSL ciphersuites:";
663         if(_ciphers.empty())
664         {
665             for(int i = 0; i < supportedCiphersSize; ++i)
666             {
667                 os << "\n " << getCipherName(supportedCiphers[i]);
668             }
669         }
670         else
671         {
672             for(vector<ALG_ID>::const_iterator i = _ciphers.begin(); i != _ciphers.end(); ++i)
673             {
674                 os << "\n " << getCipherName(*i);
675             }
676         }
677         getLogger()->trace(securityTraceCategory(), os.str());
678     }
679 
680     string certStoreLocation = properties->getPropertyWithDefault(prefix + "CertStoreLocation", "CurrentUser");
681     if(certStoreLocation != "CurrentUser" && certStoreLocation != "LocalMachine")
682     {
683         getLogger()->warning("invalid IceSSL.CertStoreLocation value `" + certStoreLocation +
684                              "' adjusted to `CurrentUser'");
685         certStoreLocation = "CurrentUser";
686     }
687 
688     //
689     // Create trusted CA store with contents of CertAuthFile
690     //
691     string caFile = properties->getProperty(prefix + "CAs");
692     if(caFile.empty())
693     {
694         caFile = properties->getProperty(prefix + "CertAuthFile");
695     }
696     if(!caFile.empty() || properties->getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0)
697     {
698         _rootStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0);
699         if(!_rootStore)
700         {
701             throw PluginInitializationException(__FILE__, __LINE__,
702                                                 "IceSSL: error creating in memory certificate store:\n" +
703                                                 lastErrorToString());
704         }
705     }
706     if(!caFile.empty())
707     {
708         string resolved;
709         if(!checkPath(caFile, defaultDir, false, resolved))
710         {
711             throw PluginInitializationException(__FILE__, __LINE__,
712                                                 "IceSSL: CA certificate file not found:\n" + caFile);
713         }
714 
715         addCertificatesToStore(resolved, _rootStore);
716     }
717 
718     if(_rootStore)
719     {
720         //
721         // Create a chain engine that uses our Trusted Root Store
722         //
723 #if defined(__MINGW32__) || (defined(_MSC_VER) && (_MSC_VER <= 1500))
724         CertChainEngineConfig config;
725         memset(&config, 0, sizeof(CertChainEngineConfig));
726         config.cbSize = sizeof(CertChainEngineConfig);
727 #else
728         CERT_CHAIN_ENGINE_CONFIG config;
729         memset(&config, 0, sizeof(CERT_CHAIN_ENGINE_CONFIG));
730         config.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG);
731 #endif
732         config.hExclusiveRoot = _rootStore;
733 
734         //
735         // Build the chain using the LocalMachine registry location as opposed
736         // to the CurrentUser location.
737         //
738         if(certStoreLocation == "LocalMachine")
739         {
740             config.dwFlags = CERT_CHAIN_USE_LOCAL_MACHINE_STORE;
741         }
742 
743 #if defined(__MINGW32__) || (defined(_MSC_VER) && (_MSC_VER <= 1500))
744         if(!CertCreateCertificateChainEngine(reinterpret_cast<CERT_CHAIN_ENGINE_CONFIG*>(&config), &_chainEngine))
745 #else
746         if(!CertCreateCertificateChainEngine(&config, &_chainEngine))
747 #endif
748         {
749             throw PluginInitializationException(__FILE__, __LINE__,
750                                                 "IceSSL: error creating certificate chain engine:\n" +
751                                                 lastErrorToString());
752         }
753     }
754     else
755     {
756         _chainEngine = (certStoreLocation == "LocalMachine") ? HCCE_LOCAL_MACHINE : HCCE_CURRENT_USER;
757     }
758 
759     string certFile = properties->getProperty(prefix + "CertFile");
760     string keyFile = properties->getProperty(prefix + "KeyFile");
761     string findCert = properties->getProperty("IceSSL.FindCert");
762 
763     if(!certFile.empty())
764     {
765         vector<string> certFiles;
766         if(!splitString(certFile, IceUtilInternal::pathsep, certFiles) || certFiles.size() > 2)
767         {
768             throw PluginInitializationException(__FILE__, __LINE__,
769                                                 "IceSSL: invalid value for " + prefix + "CertFile:\n" + certFile);
770         }
771 
772         vector<string> keyFiles;
773         if(!keyFile.empty())
774         {
775             if(!splitString(keyFile, IceUtilInternal::pathsep, keyFiles) || keyFiles.size() > 2)
776             {
777                 throw PluginInitializationException(__FILE__, __LINE__,
778                                                     "IceSSL: invalid value for " + prefix + "KeyFile:\n" + keyFile);
779             }
780 
781             if(certFiles.size() != keyFiles.size())
782             {
783                 throw PluginInitializationException(__FILE__, __LINE__,
784                                             "IceSSL: " + prefix + "KeyFile does not agree with " + prefix + "CertFile");
785             }
786         }
787 
788         for(size_t i = 0; i < certFiles.size(); ++i)
789         {
790             string cFile = certFiles[i];
791             string resolved;
792             if(!checkPath(cFile, defaultDir, false, resolved))
793             {
794                 throw PluginInitializationException(__FILE__, __LINE__,
795                                                     "IceSSL: certificate file not found:\n" + cFile);
796             }
797             cFile = resolved;
798 
799             vector<char> buffer;
800             readFile(cFile, buffer);
801             if(buffer.empty())
802             {
803                 throw PluginInitializationException(__FILE__, __LINE__,
804                                                     "IceSSL: certificate file is empty:\n" + cFile);
805             }
806 
807             CRYPT_DATA_BLOB pfxBlob;
808             pfxBlob.cbData = static_cast<DWORD>(buffer.size());
809             pfxBlob.pbData = reinterpret_cast<BYTE*>(&buffer[0]);
810 
811             HCERTSTORE store = 0;
812             PCCERT_CONTEXT cert = 0;
813             int err = 0;
814             int count = 0;
815             DWORD importFlags = (certStoreLocation == "LocalMachine") ? CRYPT_MACHINE_KEYSET : CRYPT_USER_KEYSET;
816             do
817             {
818                 string s = password(false);
819                 store = PFXImportCertStore(&pfxBlob, Ice::stringToWstring(s).c_str(), importFlags);
820                 err = store ? 0 : GetLastError();
821             }
822             while(err == ERROR_INVALID_PASSWORD && passwordPrompt && ++count < passwordRetryMax);
823 
824             if(store)
825             {
826                 //
827                 // Try to find a certificate chain.
828                 //
829                 CERT_CHAIN_FIND_BY_ISSUER_PARA para;
830                 memset(&para, 0, sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA));
831                 para.cbSize = sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA);
832 
833                 DWORD ff = CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG; // Don't fetch anything from the Internet
834                 PCCERT_CHAIN_CONTEXT chain = 0;
835                 while(!cert)
836                 {
837                     chain = CertFindChainInStore(store, X509_ASN_ENCODING, ff, CERT_CHAIN_FIND_BY_ISSUER, &para, chain);
838                     if(!chain)
839                     {
840                         break; // No more chains found in the store.
841                     }
842 
843                     if(chain->cChain > 0 && chain->rgpChain[0]->cElement > 0)
844                     {
845                         cert = CertDuplicateCertificateContext(chain->rgpChain[0]->rgpElement[0]->pCertContext);
846                     }
847                     CertFreeCertificateChain(chain);
848                 }
849 
850                 //
851                 // Check if we can find a certificate if we couldn't find a chain.
852                 //
853                 if(!cert)
854                 {
855                     cert = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, 0, cert);
856                 }
857                 if(!cert)
858                 {
859                     throw PluginInitializationException(__FILE__, __LINE__,
860                                                         "IceSSL: certificate error:\n" + lastErrorToString());
861                 }
862                 _allCerts.push_back(cert);
863                 _stores.push_back(store);
864                 continue;
865             }
866 
867             assert(err);
868             if(err != CRYPT_E_BAD_ENCODE)
869             {
870                 throw PluginInitializationException(__FILE__, __LINE__,
871                                     "IceSSL: error decoding certificate:\n" + lastErrorToString());
872             }
873 
874             //
875             // Try to load certificate & key as PEM files.
876             //
877             if(keyFiles.empty())
878             {
879                 throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: no key file specified");
880             }
881 
882             err = 0;
883             keyFile = keyFiles[i];
884             if(!checkPath(keyFile, defaultDir, false, resolved))
885             {
886                 throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: key file not found:\n" + keyFile);
887             }
888             keyFile = resolved;
889 
890             readFile(keyFile, buffer);
891             if(buffer.empty())
892             {
893                 throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: key file is empty:\n" + keyFile);
894             }
895 
896             vector<BYTE> outBuffer;
897             outBuffer.resize(buffer.size());
898             DWORD outLength = static_cast<DWORD>(buffer.size());
899 
900             //
901             // Convert the PEM encoded buffer to DER binary format.
902             //
903             if(!CryptStringToBinary(&buffer[0], static_cast<DWORD>(buffer.size()), CRYPT_STRING_BASE64HEADER,
904                                     &outBuffer[0], &outLength, 0, 0))
905             {
906                 throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error decoding key `" + keyFile +
907                                                     "':\n" + lastErrorToString());
908             }
909 
910             PCRYPT_PRIVATE_KEY_INFO keyInfo = 0;
911             BYTE* key = 0;
912             HCRYPTKEY hKey = 0;
913             try
914             {
915                 //
916                 // First try to decode as a PKCS#8 key, if that fails try PKCS#1.
917                 //
918                 DWORD decodedLength = 0;
919                 if(CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, &outBuffer[0], outLength,
920                                        CRYPT_DECODE_ALLOC_FLAG, 0, &keyInfo, &decodedLength))
921                 {
922                     //
923                     // Check that we are using a RSA Key
924                     //
925                     if(strcmp(keyInfo->Algorithm.pszObjId, szOID_RSA_RSA))
926                     {
927                         throw PluginInitializationException(__FILE__, __LINE__,
928                                                         string("IceSSL: error unknow key algorithm: `") +
929                                                             keyInfo->Algorithm.pszObjId + "'");
930                     }
931 
932                     //
933                     // Decode the private key BLOB
934                     //
935                     if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
936                                             keyInfo->PrivateKey.pbData, keyInfo->PrivateKey.cbData,
937                                             CRYPT_DECODE_ALLOC_FLAG, 0, &key, &outLength))
938                     {
939                         throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error decoding key `" +
940                                                             keyFile + "':\n" + lastErrorToString());
941                     }
942                     LocalFree(keyInfo);
943                     keyInfo = 0;
944                 }
945                 else
946                 {
947                     //
948                     // Decode the private key BLOB
949                     //
950                     if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
951                                             &outBuffer[0], outLength, CRYPT_DECODE_ALLOC_FLAG, 0, &key, &outLength))
952                     {
953                         throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error decoding key `" +
954                                                             keyFile + "':\n" + lastErrorToString());
955                     }
956                 }
957 
958                 //
959                 // Create a new RSA key set to store our key
960                 //
961                 const wstring keySetName = Ice::stringToWstring(generateUUID());
962                 HCRYPTPROV cryptProv = 0;
963 
964                 DWORD contextFlags = CRYPT_NEWKEYSET;
965                 if(certStoreLocation == "LocalMachine")
966                 {
967                     contextFlags |= CRYPT_MACHINE_KEYSET;
968                 }                                                                   ;
969 
970                 if(!CryptAcquireContextW(&cryptProv, keySetName.c_str(), MS_ENHANCED_PROV_W, PROV_RSA_FULL,
971                                          contextFlags))
972                 {
973                     throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error acquiring cryptographic "
974                                                         "context:\n" + lastErrorToString());
975                 }
976 
977                 //
978                 // Import the private key
979                 //
980                 if(!CryptImportKey(cryptProv, key, outLength, 0, 0, &hKey))
981                 {
982                     throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error importing key `" + keyFile +
983                                                         "':\n" + lastErrorToString());
984                 }
985                 LocalFree(key);
986                 key = 0;
987 
988                 CryptDestroyKey(hKey);
989                 hKey = 0;
990 
991                 //
992                 // Create a new memory store to place the certificate
993                 //
994                 store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0);
995                 if(!store)
996                 {
997                     throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error creating certificate "
998                                                         "store:\n" + lastErrorToString());
999                 }
1000 
1001                 addCertificatesToStore(cFile, store, &cert);
1002 
1003                 //
1004                 // Associate key & certificate
1005                 //
1006                 CRYPT_KEY_PROV_INFO keyProvInfo;
1007                 memset(&keyProvInfo, 0, sizeof(keyProvInfo));
1008                 keyProvInfo.pwszContainerName = const_cast<wchar_t*>(keySetName.c_str());
1009                 keyProvInfo.pwszProvName = const_cast<wchar_t*>(MS_DEF_PROV_W);
1010                 keyProvInfo.dwProvType = PROV_RSA_FULL;
1011                 keyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
1012                 if(!CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &keyProvInfo))
1013                 {
1014                     throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error seting certificate "
1015                                                         "property:\n" + lastErrorToString());
1016                 }
1017 
1018                 _importedCerts.push_back(cert);
1019                 _allCerts.push_back(cert);
1020                 _stores.push_back(store);
1021             }
1022             catch(...)
1023             {
1024                 if(keyInfo)
1025                 {
1026                     LocalFree(keyInfo);
1027                 }
1028 
1029                 if(key)
1030                 {
1031                     LocalFree(key);
1032                 }
1033 
1034                 if(hKey)
1035                 {
1036                     CryptDestroyKey(hKey);
1037                 }
1038 
1039                 if(cert)
1040                 {
1041                     CertFreeCertificateContext(cert);
1042                 }
1043 
1044                 if(store)
1045                 {
1046                     CertCloseStore(store, 0);
1047                 }
1048                 throw;
1049             }
1050         }
1051     }
1052     else if(!findCert.empty())
1053     {
1054         string certStore = properties->getPropertyWithDefault(prefix + "CertStore", "My");
1055         vector<PCCERT_CONTEXT> certs = findCertificates(certStoreLocation, certStore, findCert, _stores);
1056         if(certs.empty())
1057         {
1058             throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: no certificates found");
1059         }
1060         _allCerts.insert(_allCerts.end(), certs.begin(), certs.end());
1061     }
1062     _initialized = true;
1063 }
1064 
1065 string
getCipherName(ALG_ID cipher) const1066 SChannel::SSLEngine::getCipherName(ALG_ID cipher) const
1067 {
1068     switch(cipher)
1069     {
1070         case CALG_3DES:
1071             return "3DES";
1072         case CALG_3DES_112:
1073             return "3DES_112";
1074         case CALG_AES:
1075             return "AES";
1076         case CALG_AES_128:
1077             return "AES_128";
1078         case CALG_AES_192:
1079             return "AES_192";
1080         case CALG_AES_256:
1081             return "AES_256";
1082         case CALG_AGREEDKEY_ANY:
1083             return "AGREEDKEY_ANY";
1084         case CALG_CYLINK_MEK:
1085             return "CYLINK_MEK";
1086         case CALG_DES:
1087             return "DES";
1088         case CALG_DESX:
1089             return "DESX";
1090         case CALG_DH_EPHEM:
1091             return "DH_EPHEM";
1092         case CALG_DH_SF:
1093             return "DH_SF";
1094         case CALG_DSS_SIGN:
1095             return "DSS_SIGN";
1096         case CALG_ECDH:
1097             return "ECDH";
1098         case ICESSL_CALG_ECDH_EPHEM:
1099             return "ECDH_EPHEM";
1100         case CALG_ECDSA:
1101             return "ECDSA";
1102         case CALG_HASH_REPLACE_OWF:
1103             return "HASH_REPLACE_OWF";
1104         case CALG_HUGHES_MD5:
1105             return "HUGHES_MD5";
1106         case CALG_HMAC:
1107             return "HMAC";
1108         case CALG_MAC:
1109             return "MAC";
1110         case CALG_MD2:
1111             return "MD2";
1112         case CALG_MD4:
1113             return "MD4";
1114         case CALG_MD5:
1115             return "MD5";
1116         case CALG_NO_SIGN:
1117             return "NO_SIGN";
1118         case CALG_RC2:
1119             return "RC2";
1120         case CALG_RC4:
1121             return "RC4";
1122         case CALG_RC5:
1123             return "RC5";
1124         case CALG_RSA_KEYX:
1125             return "RSA_KEYX";
1126         case CALG_RSA_SIGN:
1127             return "RSA_SIGN";
1128         case CALG_SHA1:
1129             return "SHA1";
1130         case CALG_SHA_256:
1131             return "SHA_256";
1132         case CALG_SHA_384:
1133             return "SHA_384";
1134         case CALG_SHA_512:
1135             return "SHA_512";
1136         default:
1137         {
1138             ostringstream os;
1139             os << "Unknown cipher: " << cipher;
1140             return os.str();
1141         }
1142     }
1143 }
1144 
1145 CredHandle
newCredentialsHandle(bool incoming)1146 SChannel::SSLEngine::newCredentialsHandle(bool incoming)
1147 {
1148     SCHANNEL_CRED cred;
1149     memset(&cred, 0, sizeof(cred));
1150     cred.dwVersion = SCHANNEL_CRED_VERSION;
1151 
1152     if(!_allCerts.empty())
1153     {
1154         cred.cCreds = static_cast<DWORD>(_allCerts.size());
1155         cred.paCred = &_allCerts[0];
1156     }
1157 
1158     cred.grbitEnabledProtocols = _protocols;
1159 
1160     if(incoming)
1161     {
1162         //
1163         // Don't set SCH_SEND_ROOT_CERT as it seems to cause problems with
1164         // Java certificate validation and SChannel doesn't seems to send
1165         // the root certificate either way.
1166         //
1167         cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER;
1168 
1169         //
1170         // There's no way to prevent SChannel from sending "CA names" to the
1171         // client. Recent Windows versions don't CA names but older ones do
1172         // send all the trusted root CA names. We provide the root store to
1173         // ensure that for these older Windows versions, we also include the
1174         // CA names of our trusted roots.
1175         //
1176         cred.hRootStore = _rootStore;
1177     }
1178     else
1179     {
1180         cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS;
1181     }
1182 
1183     if(_strongCrypto)
1184     {
1185         cred.dwFlags |= SCH_USE_STRONG_CRYPTO;
1186     }
1187 
1188     if(!_ciphers.empty())
1189     {
1190         cred.cSupportedAlgs = static_cast<DWORD>(_ciphers.size());
1191         cred.palgSupportedAlgs = &_ciphers[0];
1192     }
1193 
1194     CredHandle credHandle;
1195     memset(&credHandle, 0, sizeof(credHandle));
1196 
1197     SECURITY_STATUS err = AcquireCredentialsHandle(0, const_cast<char*>(UNISP_NAME),
1198                                                    (incoming ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND),
1199                                                    0, &cred, 0, 0, &credHandle, 0);
1200 
1201     if(err != SEC_E_OK)
1202     {
1203         throw SecurityException(__FILE__, __LINE__,
1204                         "IceSSL: failed to acquire credentials handle:\n" + lastErrorToString());
1205     }
1206     return credHandle;
1207 }
1208 
1209 HCERTCHAINENGINE
chainEngine() const1210 SChannel::SSLEngine::chainEngine() const
1211 {
1212     return _chainEngine;
1213 }
1214 
1215 void
parseCiphers(const std::string & ciphers)1216 SChannel::SSLEngine::parseCiphers(const std::string& ciphers)
1217 {
1218     vector<string> tokens;
1219     splitString(ciphers, " \t", tokens);
1220     for(vector<string>::const_iterator i = tokens.begin(); i != tokens.end(); ++i)
1221     {
1222         ALG_ID id = algorithmId(*i);
1223         if(id == 0)
1224         {
1225             throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: no such cipher " + *i);
1226         }
1227         _ciphers.push_back(id);
1228     }
1229 }
1230 
1231 void
destroy()1232 SChannel::SSLEngine::destroy()
1233 {
1234     if(_chainEngine && _chainEngine != HCCE_CURRENT_USER && _chainEngine != HCCE_LOCAL_MACHINE)
1235     {
1236         CertFreeCertificateChainEngine(_chainEngine);
1237     }
1238 
1239     if(_rootStore)
1240     {
1241         CertCloseStore(_rootStore, 0);
1242     }
1243 
1244     for(vector<PCCERT_CONTEXT>::const_iterator i = _importedCerts.begin(); i != _importedCerts.end(); ++i)
1245     {
1246         //
1247         // Retrieve the certificate CERT_KEY_PROV_INFO_PROP_ID property, we use the CRYPT_KEY_PROV_INFO
1248         // data to remove the key set associated with the certificate.
1249         //
1250         DWORD length = 0;
1251         if(!CertGetCertificateContextProperty(*i, CERT_KEY_PROV_INFO_PROP_ID, 0, &length))
1252         {
1253             continue;
1254         }
1255         vector<char> buf(length);
1256         if(!CertGetCertificateContextProperty(*i, CERT_KEY_PROV_INFO_PROP_ID, &buf[0], &length))
1257         {
1258             continue;
1259         }
1260         CRYPT_KEY_PROV_INFO* key = reinterpret_cast<CRYPT_KEY_PROV_INFO*>(&buf[0]);
1261         HCRYPTPROV prov = 0;
1262         CryptAcquireContextW(&prov, key->pwszContainerName, key->pwszProvName, key->dwProvType, CRYPT_DELETEKEYSET);
1263     }
1264 
1265     for(vector<PCCERT_CONTEXT>::const_iterator i = _allCerts.begin(); i != _allCerts.end(); ++i)
1266     {
1267         CertFreeCertificateContext(*i);
1268     }
1269 
1270     for(vector<HCERTSTORE>::const_iterator i = _stores.begin(); i != _stores.end(); ++i)
1271     {
1272         CertCloseStore(*i, 0);
1273     }
1274 }
1275 
1276 void
verifyPeer(const string & address,const IceSSL::ConnectionInfoPtr & info,const string & desc)1277 SChannel::SSLEngine::verifyPeer(const string& address, const IceSSL::ConnectionInfoPtr& info, const string& desc)
1278 {
1279     verifyPeerCertName(address, info);
1280     IceSSL::SSLEngine::verifyPeer(address, info, desc);
1281 }
1282 
1283 IceInternal::TransceiverPtr
createTransceiver(const InstancePtr & instance,const IceInternal::TransceiverPtr & delegate,const string & hostOrAdapterName,bool incoming)1284 SChannel::SSLEngine::createTransceiver(const InstancePtr& instance,
1285                                        const IceInternal::TransceiverPtr& delegate,
1286                                        const string& hostOrAdapterName,
1287                                        bool incoming)
1288 {
1289     return new SChannel::TransceiverI(instance, delegate, hostOrAdapterName, incoming);
1290 }
1291