~ubuntu-branches/ubuntu/quantal/zeroc-ice/quantal

« back to all changes in this revision

Viewing changes to .pc/20-kfreebsd.patch/cpp/src/IceSSL/Instance.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cleto Martin Angelina
  • Date: 2011-04-25 18:44:24 UTC
  • mfrom: (6.1.14 sid)
  • Revision ID: james.westby@ubuntu.com-20110425184424-sep9i9euu434vq4c
Tags: 3.4.1-7
* Bug fix: "libdb5.1-java.jar was renamed to db.jar", thanks to Ondřej
  Surý (Closes: #623555).
* Bug fix: "causes noise in php5", thanks to Jayen Ashar (Closes:
  #623533).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// **********************************************************************
 
2
//
 
3
// Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved.
 
4
//
 
5
// This copy of Ice is licensed to you under the terms described in the
 
6
// ICE_LICENSE file included in this distribution.
 
7
//
 
8
// **********************************************************************
 
9
 
 
10
#include <IceUtil/Config.h>
 
11
#ifdef _WIN32
 
12
#   include <winsock2.h>
 
13
#endif
 
14
 
 
15
#include <IceSSL/Instance.h>
 
16
#include <IceSSL/EndpointI.h>
 
17
#include <IceSSL/Util.h>
 
18
#include <IceSSL/TrustManager.h>
 
19
 
 
20
#include <Ice/Communicator.h>
 
21
#include <Ice/LocalException.h>
 
22
#include <Ice/Logger.h>
 
23
#include <Ice/LoggerUtil.h>
 
24
#include <Ice/Properties.h>
 
25
#include <Ice/ProtocolPluginFacade.h>
 
26
#include <Ice/StringConverter.h>
 
27
 
 
28
#include <IceUtil/Mutex.h>
 
29
#include <IceUtil/MutexPtrLock.h>
 
30
#include <IceUtil/StringUtil.h>
 
31
 
 
32
#include <openssl/rand.h>
 
33
#include <openssl/err.h>
 
34
 
 
35
#include <IceUtil/DisableWarnings.h>
 
36
 
 
37
using namespace std;
 
38
using namespace Ice;
 
39
using namespace IceSSL;
 
40
 
 
41
IceUtil::Shared* IceInternal::upCast(IceSSL::Instance* p) { return p; }
 
42
 
 
43
namespace
 
44
{
 
45
 
 
46
IceUtil::Mutex* staticMutex = 0;
 
47
int instanceCount = 0;
 
48
IceUtil::Mutex* locks = 0;
 
49
 
 
50
class Init
 
51
{
 
52
public:
 
53
 
 
54
    Init()
 
55
    {
 
56
        staticMutex = new IceUtil::Mutex;
 
57
    }
 
58
 
 
59
    ~Init()
 
60
    {
 
61
        delete staticMutex;
 
62
        staticMutex = 0;
 
63
 
 
64
        if(locks)
 
65
        {
 
66
            delete[] locks;
 
67
            locks = 0;
 
68
        }
 
69
    }
 
70
};
 
71
 
 
72
Init init;
 
73
 
 
74
}
 
75
 
 
76
extern "C"
 
77
{
 
78
 
 
79
//
 
80
// OpenSSL mutex callback.
 
81
//
 
82
void
 
83
IceSSL_opensslLockCallback(int mode, int n, const char* file, int line)
 
84
{
 
85
    assert(locks);
 
86
    if(mode & CRYPTO_LOCK)
 
87
    {
 
88
        locks[n].lock();
 
89
    }
 
90
    else
 
91
    {
 
92
        locks[n].unlock();
 
93
    }
 
94
}
 
95
 
 
96
//
 
97
// OpenSSL thread id callback.
 
98
//
 
99
unsigned long
 
100
IceSSL_opensslThreadIdCallback()
 
101
{
 
102
#if defined(_WIN32)
 
103
    return static_cast<unsigned long>(GetCurrentThreadId());
 
104
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__osf1__)
 
105
    //
 
106
    // On some platforms, pthread_t is a pointer to a per-thread structure.
 
107
    //
 
108
    return reinterpret_cast<unsigned long>(pthread_self());
 
109
#elif (defined(__linux) || defined(__sun) || defined(__hpux)) || defined(_AIX)
 
110
    //
 
111
    // On Linux, Solaris, HP-UX and AIX, pthread_t is an integer.
 
112
    //
 
113
    return static_cast<unsigned long>(pthread_self());
 
114
#else
 
115
#   error "Unknown platform"
 
116
#endif
 
117
}
 
118
 
 
119
int
 
120
IceSSL_opensslPasswordCallback(char* buf, int size, int flag, void* userData)
 
121
{
 
122
    IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(userData);
 
123
    string passwd = p->password(flag == 1);
 
124
    int sz = static_cast<int>(passwd.size());
 
125
    if(sz > size)
 
126
    {
 
127
        sz = size - 1;
 
128
    }
 
129
    strncpy(buf, passwd.c_str(), sz);
 
130
    buf[sz] = '\0';
 
131
 
 
132
    for(string::iterator i = passwd.begin(); i != passwd.end(); ++i)
 
133
    {
 
134
        *i = '\0';
 
135
    } 
 
136
 
 
137
    return sz;
 
138
}
 
139
 
 
140
#ifndef OPENSSL_NO_DH
 
141
DH*
 
142
IceSSL_opensslDHCallback(SSL* ssl, int /*isExport*/, int keyLength)
 
143
{
 
144
    IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
 
145
    return p->dhParams(keyLength);
 
146
}
 
147
#endif
 
148
 
 
149
int
 
150
IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx)
 
151
{
 
152
    SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
 
153
    IceSSL::Instance* p = reinterpret_cast<IceSSL::Instance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
 
154
    return p->verifyCallback(ok, ssl, ctx);
 
155
}
 
156
 
 
157
}
 
158
 
 
159
static bool
 
160
passwordError()
 
161
{
 
162
    int reason = ERR_GET_REASON(ERR_peek_error());
 
163
    return (reason == PEM_R_BAD_BASE64_DECODE ||
 
164
            reason == PEM_R_BAD_DECRYPT ||
 
165
            reason == PEM_R_BAD_PASSWORD_READ ||
 
166
            reason == PEM_R_PROBLEMS_GETTING_PASSWORD);
 
167
}
 
168
 
 
169
IceSSL::Instance::Instance(const CommunicatorPtr& communicator) :
 
170
    _logger(communicator->getLogger()),
 
171
    _initialized(false),
 
172
    _ctx(0)
 
173
{
 
174
    __setNoDelete(true);
 
175
 
 
176
    //
 
177
    // Initialize OpenSSL if necessary.
 
178
    //
 
179
    IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
 
180
    instanceCount++;
 
181
 
 
182
    if(instanceCount == 1)
 
183
    {
 
184
        PropertiesPtr properties = communicator->getProperties();
 
185
 
 
186
        //
 
187
        // Create the mutexes and set the callbacks.
 
188
        //
 
189
        if(!locks)
 
190
        {
 
191
            locks = new IceUtil::Mutex[CRYPTO_num_locks()];
 
192
            CRYPTO_set_locking_callback(IceSSL_opensslLockCallback);
 
193
            CRYPTO_set_id_callback(IceSSL_opensslThreadIdCallback);
 
194
        }
 
195
 
 
196
        //
 
197
        // Load human-readable error messages.
 
198
        //
 
199
        SSL_load_error_strings();
 
200
 
 
201
        //
 
202
        // Initialize the SSL library.
 
203
        //
 
204
        SSL_library_init();
 
205
 
 
206
        //
 
207
        // This is necessary to allow programs that use OpenSSL 0.9.x to
 
208
        // load private key files generated by OpenSSL 1.x.
 
209
        //
 
210
        OpenSSL_add_all_algorithms();
 
211
 
 
212
        //
 
213
        // Initialize the PRNG.
 
214
        //
 
215
#ifdef WINDOWS
 
216
        RAND_screen(); // Uses data from the screen if possible.
 
217
#endif
 
218
        char randFile[1024];
 
219
        if(RAND_file_name(randFile, sizeof(randFile))) // Gets the name of a default seed file.
 
220
        {
 
221
            RAND_load_file(randFile, 1024);
 
222
        }
 
223
        string randFiles = Ice::nativeToUTF8(communicator, properties->getProperty("IceSSL.Random"));
 
224
 
 
225
        if(!randFiles.empty())
 
226
        {
 
227
            vector<string> files;
 
228
#ifdef _WIN32
 
229
            const string sep = ";";
 
230
#else
 
231
            const string sep = ":";
 
232
#endif
 
233
            string defaultDir = Ice::nativeToUTF8(communicator, properties->getProperty("IceSSL.DefaultDir"));
 
234
 
 
235
            if(!IceUtilInternal::splitString(randFiles, sep, files))
 
236
            {
 
237
                PluginInitializationException ex(__FILE__, __LINE__);
 
238
                ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles;
 
239
                throw ex;
 
240
            }
 
241
            for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
 
242
            {
 
243
                string file = *p;
 
244
                if(!checkPath(file, defaultDir, false))
 
245
                {
 
246
                    PluginInitializationException ex(__FILE__, __LINE__);
 
247
                    ex.reason = "IceSSL: entropy data file not found:\n" + file;
 
248
                    throw ex;
 
249
                }
 
250
                if(!RAND_load_file(file.c_str(), 1024))
 
251
                {
 
252
                    PluginInitializationException ex(__FILE__, __LINE__);
 
253
                    ex.reason = "IceSSL: unable to load entropy data from " + file;
 
254
                    throw ex;
 
255
                }
 
256
            }
 
257
        }
 
258
#ifndef _WIN32
 
259
        //
 
260
        // The Entropy Gathering Daemon (EGD) is not available on Windows.
 
261
        // The file should be a Unix domain socket for the daemon.
 
262
        //
 
263
        string entropyDaemon = properties->getProperty("IceSSL.EntropyDaemon");
 
264
        if(!entropyDaemon.empty())
 
265
        {
 
266
            if(RAND_egd(entropyDaemon.c_str()) <= 0)
 
267
            {
 
268
                PluginInitializationException ex(__FILE__, __LINE__);
 
269
                ex.reason = "IceSSL: EGD failure using file " + entropyDaemon;
 
270
                throw ex;
 
271
            }
 
272
        }
 
273
#endif
 
274
        if(!RAND_status())
 
275
        {
 
276
            communicator->getLogger()->warning("IceSSL: insufficient data to initialize PRNG");
 
277
        }
 
278
    }
 
279
 
 
280
    _facade = IceInternal::getProtocolPluginFacade(communicator);
 
281
    _securityTraceLevel = communicator->getProperties()->getPropertyAsInt("IceSSL.Trace.Security");
 
282
    _securityTraceCategory = "Security";
 
283
    _trustManager = new TrustManager(communicator);
 
284
    
 
285
    //
 
286
    // Register the endpoint factory. We have to do this now, rather than
 
287
    // in initialize, because the communicator may need to interpret
 
288
    // proxies before the plug-in is fully initialized.
 
289
    //
 
290
    _facade->addEndpointFactory(new EndpointFactoryI(this));
 
291
 
 
292
    __setNoDelete(false);
 
293
}
 
294
 
 
295
IceSSL::Instance::~Instance()
 
296
{
 
297
    //
 
298
    // Clean up OpenSSL resources.
 
299
    //
 
300
    IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
 
301
 
 
302
    if(--instanceCount == 0)
 
303
    {
 
304
        //
 
305
        // NOTE: We can't destroy the locks here: threads which might have called openssl methods
 
306
        // might access openssl locks upon termination (from DllMain/THREAD_DETACHED). Instead, 
 
307
        // we release the locks in the ~Init() static destructor. See bug #4156.
 
308
        //
 
309
        //CRYPTO_set_locking_callback(0);
 
310
        //CRYPTO_set_id_callback(0);
 
311
        //delete[] locks;
 
312
        //locks = 0;
 
313
 
 
314
        CRYPTO_cleanup_all_ex_data();
 
315
        RAND_cleanup();
 
316
        ERR_free_strings();
 
317
        EVP_cleanup();
 
318
    }
 
319
}
 
320
 
 
321
void
 
322
IceSSL::Instance::initialize()
 
323
{
 
324
    if(_initialized)
 
325
    {
 
326
        return;
 
327
    }
 
328
 
 
329
    try
 
330
    {
 
331
        const string propPrefix = "IceSSL.";
 
332
        PropertiesPtr properties = communicator()->getProperties();
 
333
 
 
334
        //
 
335
        // CheckCertName determines whether we compare the name in a peer's
 
336
        // certificate against its hostname.
 
337
        //
 
338
        _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "CheckCertName", 0) > 0;
 
339
 
 
340
        //
 
341
        // VerifyDepthMax establishes the maximum length of a peer's certificate
 
342
        // chain, including the peer's certificate. A value of 0 means there is
 
343
        // no maximum.
 
344
        //
 
345
        _verifyDepthMax = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyDepthMax", 2);
 
346
 
 
347
        //
 
348
        // VerifyPeer determines whether certificate validation failures abort a connection.
 
349
        //
 
350
        _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2);
 
351
 
 
352
        //
 
353
        // Create an SSL context if the application hasn't supplied one.
 
354
        //
 
355
        if(!_ctx)
 
356
        {
 
357
            _ctx = SSL_CTX_new(SSLv23_method());
 
358
            if(!_ctx)
 
359
            {
 
360
                PluginInitializationException ex(__FILE__, __LINE__);
 
361
                ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors();
 
362
                throw ex;
 
363
            }
 
364
 
 
365
            //
 
366
            // Check for a default directory. We look in this directory for
 
367
            // files mentioned in the configuration.
 
368
            //
 
369
            string defaultDir = properties->getProperty(propPrefix + "DefaultDir");
 
370
 
 
371
            //
 
372
            // If the configuration defines a password, or the application has supplied
 
373
            // a password prompt object, then register a password callback. Otherwise,
 
374
            // let OpenSSL use its default behavior.
 
375
            //
 
376
            {
 
377
                // TODO: Support quoted value?
 
378
                string password = properties->getProperty(propPrefix + "Password");
 
379
                if(!password.empty() || _prompt)
 
380
                {
 
381
                    SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback);
 
382
                    SSL_CTX_set_default_passwd_cb_userdata(_ctx, this);
 
383
                    _password = password;
 
384
                }
 
385
            }
 
386
 
 
387
            int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3);
 
388
 
 
389
            //
 
390
            // Establish the location of CA certificates.
 
391
            //
 
392
            {
 
393
                string caFile = properties->getProperty(propPrefix + "CertAuthFile");
 
394
                string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir);
 
395
                const char* file = 0;
 
396
                const char* dir = 0;
 
397
                if(!caFile.empty())
 
398
                {
 
399
                    if(!checkPath(caFile, defaultDir, false))
 
400
                    {
 
401
                        PluginInitializationException ex(__FILE__, __LINE__);
 
402
                        ex.reason = "IceSSL: CA certificate file not found:\n" + caFile;
 
403
                        throw ex;
 
404
                    }
 
405
                    file = caFile.c_str();
 
406
                }
 
407
                if(!caDir.empty())
 
408
                {
 
409
                    if(!checkPath(caDir, defaultDir, true))
 
410
                    {
 
411
                        PluginInitializationException ex(__FILE__, __LINE__);
 
412
                        ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir;
 
413
                        throw ex;
 
414
                    }
 
415
                    dir = caDir.c_str();
 
416
                }
 
417
                if(file || dir)
 
418
                {
 
419
                    //
 
420
                    // The certificate may be stored in an encrypted file, so handle
 
421
                    // password retries.
 
422
                    //
 
423
                    int count = 0;
 
424
                    int err = 0;
 
425
                    while(count < passwordRetryMax)
 
426
                    {
 
427
                        ERR_clear_error();
 
428
                        err = SSL_CTX_load_verify_locations(_ctx, file, dir);
 
429
                        if(err)
 
430
                        {
 
431
                            break;
 
432
                        }
 
433
                        ++count;
 
434
                    }
 
435
                    if(err == 0)
 
436
                    {
 
437
                        string msg = "IceSSL: unable to establish CA certificates";
 
438
                        if(passwordError())
 
439
                        {
 
440
                            msg += ":\ninvalid password";
 
441
                        }
 
442
                        else
 
443
                        {
 
444
                            string err = sslErrors();
 
445
                            if(!err.empty())
 
446
                            {
 
447
                                msg += ":\n" + err;
 
448
                            }
 
449
                        }
 
450
                        PluginInitializationException ex(__FILE__, __LINE__);
 
451
                        ex.reason = msg;
 
452
                        throw ex;
 
453
                    }
 
454
                }
 
455
            }
 
456
 
 
457
            //
 
458
            // Establish the certificate chains and private keys. One RSA certificate and
 
459
            // one DSA certificate are allowed.
 
460
            //
 
461
            {
 
462
#ifdef _WIN32
 
463
                const string sep = ";";
 
464
#else
 
465
                const string sep = ":";
 
466
#endif
 
467
                string certFile = properties->getProperty(propPrefix + "CertFile");
 
468
                string keyFile = properties->getProperty(propPrefix + "KeyFile");
 
469
                vector<string>::size_type numCerts = 0;
 
470
                if(!certFile.empty())
 
471
                {
 
472
                    vector<string> files;
 
473
                    if(!IceUtilInternal::splitString(certFile, sep, files) || files.size() > 2)
 
474
                    {
 
475
                        PluginInitializationException ex(__FILE__, __LINE__);
 
476
                        ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile;
 
477
                        throw ex;
 
478
                    }
 
479
                    numCerts = files.size();
 
480
                    for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
 
481
                    {
 
482
                        string file = *p;
 
483
                        if(!checkPath(file, defaultDir, false))
 
484
                        {
 
485
                            PluginInitializationException ex(__FILE__, __LINE__);
 
486
                            ex.reason = "IceSSL: certificate file not found:\n" + file;
 
487
                            throw ex;
 
488
                        }
 
489
                        //
 
490
                        // The certificate may be stored in an encrypted file, so handle
 
491
                        // password retries.
 
492
                        //
 
493
                        int count = 0;
 
494
                        int err = 0;
 
495
                        while(count < passwordRetryMax)
 
496
                        {
 
497
                            ERR_clear_error();
 
498
                            err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str());
 
499
                            if(err)
 
500
                            {
 
501
                                break;
 
502
                            }
 
503
                            ++count;
 
504
                        }
 
505
                        if(err == 0)
 
506
                        {
 
507
                            string msg = "IceSSL: unable to load certificate chain from file " + file;
 
508
                            if(passwordError())
 
509
                            {
 
510
                                msg += ":\ninvalid password";
 
511
                            }
 
512
                            else
 
513
                            {
 
514
                                string err = sslErrors();
 
515
                                if(!err.empty())
 
516
                                {
 
517
                                    msg += ":\n" + err;
 
518
                                }
 
519
                            }
 
520
                            PluginInitializationException ex(__FILE__, __LINE__);
 
521
                            ex.reason = msg;
 
522
                            throw ex;
 
523
                        }
 
524
                    }
 
525
                }
 
526
                if(keyFile.empty())
 
527
                {
 
528
                    keyFile = certFile; // Assume the certificate file also contains the private key.
 
529
                }
 
530
                if(!keyFile.empty())
 
531
                {
 
532
                    vector<string> files;
 
533
                    if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2)
 
534
                    {
 
535
                        PluginInitializationException ex(__FILE__, __LINE__);
 
536
                        ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile;
 
537
                        throw ex;
 
538
                    }
 
539
                    if(files.size() != numCerts)
 
540
                    {
 
541
                        PluginInitializationException ex(__FILE__, __LINE__);
 
542
                        ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile";
 
543
                        throw ex;
 
544
                    }
 
545
                    for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
 
546
                    {
 
547
                        string file = *p;
 
548
                        if(!checkPath(file, defaultDir, false))
 
549
                        {
 
550
                            PluginInitializationException ex(__FILE__, __LINE__);
 
551
                            ex.reason = "IceSSL: key file not found:\n" + file;
 
552
                            throw ex;
 
553
                        }
 
554
                        //
 
555
                        // The private key may be stored in an encrypted file, so handle
 
556
                        // password retries.
 
557
                        //
 
558
                        int count = 0;
 
559
                        int err = 0;
 
560
                        while(count < passwordRetryMax)
 
561
                        {
 
562
                            ERR_clear_error();
 
563
                            err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM);
 
564
                            if(err)
 
565
                            {
 
566
                                break;
 
567
                            }
 
568
                            ++count;
 
569
                        }
 
570
                        if(err == 0)
 
571
                        {
 
572
                            string msg = "IceSSL: unable to load private key from file " + file;
 
573
                            if(passwordError())
 
574
                            {
 
575
                                msg += ":\ninvalid password";
 
576
                            }
 
577
                            else
 
578
                            {
 
579
                                string err = sslErrors();
 
580
                                if(!err.empty())
 
581
                                {
 
582
                                    msg += ":\n" + err;
 
583
                                }
 
584
                            }
 
585
                            PluginInitializationException ex(__FILE__, __LINE__);
 
586
                            ex.reason = msg;
 
587
                            throw ex;
 
588
                        }
 
589
                    }
 
590
                    if(!SSL_CTX_check_private_key(_ctx))
 
591
                    {
 
592
                        PluginInitializationException ex(__FILE__, __LINE__);
 
593
                        ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors();
 
594
                        throw ex;
 
595
                    }
 
596
                }
 
597
            }
 
598
 
 
599
            //
 
600
            // Diffie Hellman configuration.
 
601
            //
 
602
            {
 
603
#ifndef OPENSSL_NO_DH
 
604
                _dhParams = new DHParams;
 
605
                SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_DH_USE);
 
606
                SSL_CTX_set_tmp_dh_callback(_ctx, IceSSL_opensslDHCallback);
 
607
#endif
 
608
                //
 
609
                // Properties have the following form:
 
610
                //
 
611
                // ...DH.<keyLength>=file
 
612
                //
 
613
                const string dhPrefix = propPrefix + "DH.";
 
614
                PropertyDict d = properties->getPropertiesForPrefix(dhPrefix);
 
615
                if(!d.empty())
 
616
                {
 
617
#ifdef OPENSSL_NO_DH
 
618
                    _logger->warning("IceSSL: OpenSSL is not configured for Diffie Hellman");
 
619
#else
 
620
                    for(PropertyDict::iterator p = d.begin(); p != d.end(); ++p)
 
621
                    {
 
622
                        string s = p->first.substr(dhPrefix.size());
 
623
                        int keyLength = atoi(s.c_str());
 
624
                        if(keyLength > 0)
 
625
                        {
 
626
                            string file = p->second;
 
627
                            if(!checkPath(file, defaultDir, false))
 
628
                            {
 
629
                                PluginInitializationException ex(__FILE__, __LINE__);
 
630
                                ex.reason = "IceSSL: DH parameter file not found:\n" + file;
 
631
                                throw ex;
 
632
                            }
 
633
                            if(!_dhParams->add(keyLength, file))
 
634
                            {
 
635
                                PluginInitializationException ex(__FILE__, __LINE__);
 
636
                                ex.reason = "IceSSL: unable to read DH parameter file " + file;
 
637
                                throw ex;
 
638
                            }
 
639
                        }
 
640
                    }
 
641
#endif
 
642
                }
 
643
            }
 
644
        }
 
645
 
 
646
        //
 
647
        // Store a pointer to ourself for use in OpenSSL callbacks.
 
648
        //
 
649
        SSL_CTX_set_ex_data(_ctx, 0, this);
 
650
 
 
651
        //
 
652
        // This is necessary for successful interop with Java. Without it, a Java
 
653
        // client would fail to reestablish a connection: the server gets the
 
654
        // error "session id context uninitialized" and the client receives
 
655
        // "SSLHandshakeException: Remote host closed connection during handshake".
 
656
        //
 
657
        SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_OFF);
 
658
 
 
659
        //
 
660
        // Select protocols.
 
661
        //
 
662
        StringSeq protocols = properties->getPropertyAsList(propPrefix + "Protocols");
 
663
        if(!protocols.empty())
 
664
        {
 
665
            parseProtocols(protocols);
 
666
        }
 
667
 
 
668
        //
 
669
        // Establish the cipher list.
 
670
        //
 
671
        string ciphers = properties->getProperty(propPrefix + "Ciphers");
 
672
        if(!ciphers.empty())
 
673
        {
 
674
            if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str()))
 
675
            {
 
676
                PluginInitializationException ex(__FILE__, __LINE__);
 
677
                ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors();
 
678
                throw ex;
 
679
            }
 
680
        }
 
681
 
 
682
        //
 
683
        // Determine whether a certificate is required from the peer.
 
684
        //
 
685
        {
 
686
            int sslVerifyMode;
 
687
            switch(_verifyPeer)
 
688
            {
 
689
            case 0:
 
690
                sslVerifyMode = SSL_VERIFY_NONE;
 
691
                break;
 
692
            case 1:
 
693
                sslVerifyMode = SSL_VERIFY_PEER;
 
694
                break;
 
695
            case 2:
 
696
                sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
 
697
                break;
 
698
            default:
 
699
            {
 
700
                PluginInitializationException ex(__FILE__, __LINE__);
 
701
                ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer";
 
702
                throw ex;
 
703
            }
 
704
            }
 
705
            SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback);
 
706
        }
 
707
    }
 
708
    catch(...)
 
709
    {
 
710
        //
 
711
        // We free the SSL context regardless of whether the plugin created it
 
712
        // or the application supplied it.
 
713
        //
 
714
        SSL_CTX_free(_ctx);
 
715
        _ctx = 0;
 
716
        throw;
 
717
    }
 
718
 
 
719
    _initialized = true;
 
720
}
 
721
 
 
722
void
 
723
IceSSL::Instance::context(SSL_CTX* context)
 
724
{
 
725
    if(_initialized)
 
726
    {
 
727
        PluginInitializationException ex(__FILE__, __LINE__);
 
728
        ex.reason = "IceSSL: plug-in is already initialized";
 
729
        throw ex;
 
730
    }
 
731
 
 
732
    assert(!_ctx);
 
733
    _ctx = context;
 
734
}
 
735
 
 
736
SSL_CTX*
 
737
IceSSL::Instance::context() const
 
738
{
 
739
    return _ctx;
 
740
}
 
741
 
 
742
void
 
743
IceSSL::Instance::setCertificateVerifier(const CertificateVerifierPtr& verifier)
 
744
{
 
745
    _verifier = verifier;
 
746
}
 
747
 
 
748
void
 
749
IceSSL::Instance::setPasswordPrompt(const PasswordPromptPtr& prompt)
 
750
{
 
751
    _prompt = prompt;
 
752
}
 
753
 
 
754
CommunicatorPtr
 
755
IceSSL::Instance::communicator() const
 
756
{
 
757
    return _facade->getCommunicator();
 
758
}
 
759
 
 
760
IceInternal::EndpointHostResolverPtr
 
761
IceSSL::Instance::endpointHostResolver() const
 
762
{
 
763
    return _facade->getEndpointHostResolver();
 
764
}
 
765
 
 
766
IceInternal::ProtocolSupport
 
767
IceSSL::Instance::protocolSupport() const
 
768
{
 
769
    return _facade->getProtocolSupport();
 
770
}
 
771
 
 
772
string
 
773
IceSSL::Instance::defaultHost() const
 
774
{
 
775
    return _facade->getDefaultHost();
 
776
}
 
777
 
 
778
int
 
779
IceSSL::Instance::networkTraceLevel() const
 
780
{
 
781
    return _facade->getNetworkTraceLevel();
 
782
}
 
783
 
 
784
string
 
785
IceSSL::Instance::networkTraceCategory() const
 
786
{
 
787
    return _facade->getNetworkTraceCategory();
 
788
}
 
789
 
 
790
int
 
791
IceSSL::Instance::securityTraceLevel() const
 
792
{
 
793
    return _securityTraceLevel;
 
794
}
 
795
 
 
796
string
 
797
IceSSL::Instance::securityTraceCategory() const
 
798
{
 
799
    return _securityTraceCategory;
 
800
}
 
801
 
 
802
void
 
803
IceSSL::Instance::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info)
 
804
{
 
805
    long result = SSL_get_verify_result(ssl);
 
806
    if(result != X509_V_OK)
 
807
    {
 
808
        if(_verifyPeer == 0)
 
809
        {
 
810
            if(_securityTraceLevel >= 1)
 
811
            {
 
812
                ostringstream ostr;
 
813
                ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result);
 
814
                _logger->trace(_securityTraceCategory, ostr.str());
 
815
            }
 
816
        }
 
817
        else
 
818
        {
 
819
            ostringstream ostr;
 
820
            ostr << "IceSSL: certificate verification failed:\n" << X509_verify_cert_error_string(result);
 
821
            string msg = ostr.str();
 
822
            if(_securityTraceLevel >= 1)
 
823
            {
 
824
                _logger->trace(_securityTraceCategory, msg);
 
825
            }
 
826
            SecurityException ex(__FILE__, __LINE__);
 
827
            ex.reason = msg;
 
828
            throw ex;
 
829
        }
 
830
    }
 
831
 
 
832
    X509* rawCert = SSL_get_peer_certificate(ssl);
 
833
    CertificatePtr cert;
 
834
    if(rawCert != 0)
 
835
    {
 
836
        cert = new Certificate(rawCert);
 
837
    }
 
838
 
 
839
    //
 
840
    // For an outgoing connection, we compare the proxy address (if any) against
 
841
    // fields in the server's certificate (if any).
 
842
    //
 
843
    if(cert && !address.empty())
 
844
    {
 
845
        //
 
846
        // Extract the IP addresses and the DNS names from the subject
 
847
        // alternative names.
 
848
        //
 
849
        vector<pair<int, string> > subjectAltNames = cert->getSubjectAlternativeNames();
 
850
        vector<string> ipAddresses;
 
851
        vector<string> dnsNames;
 
852
        for(vector<pair<int, string> >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p)
 
853
        {
 
854
            if(p->first == 7)
 
855
            {
 
856
                ipAddresses.push_back(IceUtilInternal::toLower(p->second));
 
857
            }
 
858
            else if(p->first == 2)
 
859
            {
 
860
                dnsNames.push_back(IceUtilInternal::toLower(p->second));
 
861
            }
 
862
        }
 
863
 
 
864
        //
 
865
        // Compare the peer's address against the common name.
 
866
        //
 
867
        bool certNameOK = false;
 
868
        string dn;
 
869
        string addrLower = IceUtilInternal::toLower(address);
 
870
        {
 
871
            DistinguishedName d = cert->getSubjectDN();
 
872
            dn = IceUtilInternal::toLower(string(d));
 
873
            string cn = "cn=" + addrLower;
 
874
            string::size_type pos = dn.find(cn);
 
875
            if(pos != string::npos)
 
876
            {
 
877
                //
 
878
                // Ensure we match the entire common name.
 
879
                //
 
880
                certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ',');
 
881
            }
 
882
        }
 
883
 
 
884
        //
 
885
        // Compare the peer's address against the the dnsName and ipAddress
 
886
        // values in the subject alternative name.
 
887
        //
 
888
        if(!certNameOK)
 
889
        {
 
890
            certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end();
 
891
        }
 
892
        if(!certNameOK)
 
893
        {
 
894
            certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end();
 
895
        }
 
896
 
 
897
        //
 
898
        // Log a message if the name comparison fails. If CheckCertName is defined,
 
899
        // we also raise an exception to abort the connection. Don't log a message if
 
900
        // CheckCertName is not defined and a verifier is present.
 
901
        //
 
902
        if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && !_verifier)))
 
903
        {
 
904
            ostringstream ostr;
 
905
            ostr << "IceSSL: ";
 
906
            if(!_checkCertName)
 
907
            {
 
908
                ostr << "ignoring ";
 
909
            }
 
910
            ostr << "certificate validation failure:\npeer certificate does not have `" << address
 
911
                 << "' as its commonName or in its subjectAltName extension";
 
912
            if(!dn.empty())
 
913
            {
 
914
                ostr << "\nSubject DN: " << dn;
 
915
            }
 
916
            if(!dnsNames.empty())
 
917
            {
 
918
                ostr << "\nDNS names found in certificate: ";
 
919
                for(vector<string>::const_iterator p = dnsNames.begin(); p != dnsNames.end(); ++p)
 
920
                {
 
921
                    if(p != dnsNames.begin())
 
922
                    {
 
923
                        ostr << ", ";
 
924
                    }
 
925
                    ostr << *p;
 
926
                }
 
927
            }
 
928
            if(!ipAddresses.empty())
 
929
            {
 
930
                ostr << "\nIP addresses found in certificate: ";
 
931
                for(vector<string>::const_iterator p = ipAddresses.begin(); p != ipAddresses.end(); ++p)
 
932
                {
 
933
                    if(p != ipAddresses.begin())
 
934
                    {
 
935
                        ostr << ", ";
 
936
                    }
 
937
                    ostr << *p;
 
938
                }
 
939
            }
 
940
            string msg = ostr.str();
 
941
            if(_securityTraceLevel >= 1)
 
942
            {
 
943
                Trace out(_logger, _securityTraceCategory);
 
944
                out << msg;
 
945
            }
 
946
            if(_checkCertName)
 
947
            {
 
948
                SecurityException ex(__FILE__, __LINE__);
 
949
                ex.reason = msg;
 
950
                throw ex;
 
951
            }
 
952
        }
 
953
    }
 
954
 
 
955
    if(_verifyDepthMax > 0 && static_cast<int>(info->certs.size()) > _verifyDepthMax)
 
956
    {
 
957
        ostringstream ostr;
 
958
        ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n"
 
959
             << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of "
 
960
             << _verifyDepthMax;
 
961
        string msg = ostr.str();
 
962
        if(_securityTraceLevel >= 1)
 
963
        {
 
964
            _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
 
965
        }
 
966
        SecurityException ex(__FILE__, __LINE__);
 
967
        ex.reason = msg;
 
968
        throw ex;
 
969
    }
 
970
 
 
971
    if(!_trustManager->verify(info))
 
972
    {
 
973
        string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager";
 
974
        if(_securityTraceLevel >= 1)
 
975
        {
 
976
            _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
 
977
        }
 
978
        SecurityException ex(__FILE__, __LINE__);
 
979
        ex.reason = msg;
 
980
        throw ex;
 
981
    }
 
982
 
 
983
    if(_verifier && !_verifier->verify(info))
 
984
    {
 
985
        string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier";
 
986
        if(_securityTraceLevel >= 1)
 
987
        {
 
988
            _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
 
989
        }
 
990
        SecurityException ex(__FILE__, __LINE__);
 
991
        ex.reason = msg;
 
992
        throw ex;
 
993
    }
 
994
}
 
995
 
 
996
string
 
997
IceSSL::Instance::sslErrors() const
 
998
{
 
999
    return getSslErrors(_securityTraceLevel >= 1);
 
1000
}
 
1001
 
 
1002
void
 
1003
IceSSL::Instance::destroy()
 
1004
{
 
1005
    _facade = 0;
 
1006
 
 
1007
    if(_ctx)
 
1008
    {
 
1009
        SSL_CTX_free(_ctx);
 
1010
    }
 
1011
}
 
1012
 
 
1013
string
 
1014
IceSSL::Instance::password(bool /*encrypting*/)
 
1015
{
 
1016
    if(_prompt)
 
1017
    {
 
1018
        try
 
1019
        {
 
1020
            return _prompt->getPassword();
 
1021
        }
 
1022
        catch(...)
 
1023
        {
 
1024
            //
 
1025
            // Don't allow exceptions to cross an OpenSSL boundary.
 
1026
            //
 
1027
            return string();
 
1028
        }
 
1029
    }
 
1030
    else
 
1031
    {
 
1032
        return _password;
 
1033
    }
 
1034
}
 
1035
 
 
1036
int
 
1037
IceSSL::Instance::verifyCallback(int ok, SSL* ssl, X509_STORE_CTX* c)
 
1038
{
 
1039
    if(!ok && _securityTraceLevel >= 1)
 
1040
    {
 
1041
        X509* cert = X509_STORE_CTX_get_current_cert(c);
 
1042
        int err = X509_STORE_CTX_get_error(c);
 
1043
        char buf[256];
 
1044
 
 
1045
        Trace out(_logger, _securityTraceCategory);
 
1046
        out << "certificate verification failure\n";
 
1047
 
 
1048
        X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf)));
 
1049
        out << "issuer = " << buf << '\n';
 
1050
        X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf)));
 
1051
        out << "subject = " << buf << '\n';
 
1052
        out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n';
 
1053
        out << "error = " << X509_verify_cert_error_string(err) << '\n';
 
1054
        out << IceInternal::fdToString(SSL_get_fd(ssl));
 
1055
    }
 
1056
    return ok;
 
1057
}
 
1058
 
 
1059
#ifndef OPENSSL_NO_DH
 
1060
DH*
 
1061
IceSSL::Instance::dhParams(int keyLength)
 
1062
{
 
1063
    return _dhParams->get(keyLength);
 
1064
}
 
1065
#endif
 
1066
 
 
1067
void
 
1068
IceSSL::Instance::traceConnection(SSL* ssl, bool incoming)
 
1069
{
 
1070
    Trace out(_logger, _securityTraceCategory);
 
1071
    out << "SSL summary for " << (incoming ? "incoming" : "outgoing") << " connection\n";
 
1072
 
 
1073
    //
 
1074
    // The const_cast is necesary because Solaris still uses OpenSSL 0.9.7.
 
1075
    //
 
1076
    //const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
 
1077
    SSL_CIPHER *cipher = const_cast<SSL_CIPHER*>(SSL_get_current_cipher(ssl));
 
1078
    if(!cipher)
 
1079
    {
 
1080
        out << "unknown cipher\n";
 
1081
    }
 
1082
    else
 
1083
    {
 
1084
        out << "cipher = " << SSL_CIPHER_get_name(cipher) << "\n";
 
1085
        out << "bits = " << SSL_CIPHER_get_bits(cipher, 0) << "\n";
 
1086
        out << "protocol = " << SSL_get_version(ssl) << "\n";
 
1087
    }
 
1088
    out << IceInternal::fdToString(SSL_get_fd(ssl));
 
1089
}
 
1090
 
 
1091
void
 
1092
IceSSL::Instance::parseProtocols(const StringSeq& protocols)
 
1093
{
 
1094
    bool sslv3 = false, tlsv1 = false;
 
1095
    for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
 
1096
    {
 
1097
        string prot = *p;
 
1098
 
 
1099
        if(prot == "ssl3" || prot == "sslv3")
 
1100
        {
 
1101
            sslv3 = true;
 
1102
        }
 
1103
        else if(prot == "tls" || prot == "tls1" || prot == "tlsv1")
 
1104
        {
 
1105
            tlsv1 = true;
 
1106
        }
 
1107
        else
 
1108
        {
 
1109
            PluginInitializationException ex(__FILE__, __LINE__);
 
1110
            ex.reason = "IceSSL: unrecognized protocol `" + prot + "'";
 
1111
            throw ex;
 
1112
        }
 
1113
    }
 
1114
 
 
1115
    long opts = SSL_OP_NO_SSLv2; // SSLv2 is not supported.
 
1116
    if(!sslv3)
 
1117
    {
 
1118
        opts |= SSL_OP_NO_SSLv3;
 
1119
    }
 
1120
    if(!tlsv1)
 
1121
    {
 
1122
        opts |= SSL_OP_NO_TLSv1;
 
1123
    }
 
1124
    SSL_CTX_set_options(_ctx, opts);
 
1125
}