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(¶, 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, ¶, 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