Uncommented beaudio code
[pwlib.git] / src / ptclib / pssl.cxx
blobc5380251322a5eca44b868efde5b397b4f964011
1 /*
2 * pssl.cxx
4 * SSL implementation for PTLib using the SSLeay package
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * Portions bsed upon the file crypto/buffer/bss_sock.c
27 * Original copyright notice appears below
29 * $Id$
30 * $Log$
31 * Revision 1.38 2004/02/23 23:52:20 csoutheren
32 * Added pragmas to avoid every Windows application needing to include libs explicitly
34 * Revision 1.37 2004/02/22 01:57:37 ykiryanov
35 * Put a fix for a compiler choke on BeOS when calling macro d2i_DHparams_bio in PSSLDiffieHellman::Load. Fast on Monday.
37 * Revision 1.36 2003/04/16 08:00:19 robertj
38 * Windoes psuedo autoconf support
40 * Revision 1.35 2002/11/06 22:47:25 robertj
41 * Fixed header comment (copyright etc)
43 * Revision 1.34 2002/06/07 02:55:23 robertj
44 * Fixed GNU warning
46 * Revision 1.33 2002/04/02 16:59:35 robertj
47 * Fixed GNU warning
49 * Revision 1.32 2002/04/02 16:17:28 robertj
50 * Fixed bug where returned TRUE when read count 0 or write count not len
51 * which is not according to Read()/Write() semantics. Could cause high CPU
52 * loops when sockets shutdown.
54 * Revision 1.31 2002/03/28 07:26:25 robertj
55 * Added Diffie-Hellman parameters wrapper class.
57 * Revision 1.30 2001/12/13 09:15:41 robertj
58 * Added function to get private key as ray DER binary data or as base64 string.
60 * Revision 1.29 2001/12/06 04:06:03 robertj
61 * Removed "Win32 SSL xxx" build configurations in favour of system
62 * environment variables to select optional libraries.
64 * Revision 1.28 2001/12/04 02:59:18 robertj
65 * Fixed problem on platforms where a pointer is not the same size as an int.
67 * Revision 1.27 2001/10/31 01:31:26 robertj
68 * Added enhancements for saving/loading/creating certificates and keys.
70 * Revision 1.26 2001/09/28 08:50:48 robertj
71 * Fixed bug where last count not set to zero if have error on read/write.
73 * Revision 1.25 2001/09/10 02:51:23 robertj
74 * Major change to fix problem with error codes being corrupted in a
75 * PChannel when have simultaneous reads and writes in threads.
77 * Revision 1.24 2001/06/01 00:53:59 robertj
78 * Added certificate constructor that takes a PBYTEArray
80 * Revision 1.23 2001/05/31 07:04:11 craigs
81 * Changed random seed function
83 * Revision 1.22 2001/05/29 03:33:54 craigs
84 * Added options to be compatible with OpenSSL 0.9.6
86 * Revision 1.21 2001/05/16 06:31:37 robertj
87 * Fixed GNU C++ compatibility
89 * Revision 1.20 2001/05/16 06:02:37 craigs
90 * Changed to allow detection of non-SSL connection to SecureHTTPServiceProcess
92 * Revision 1.19 2001/05/09 07:00:22 robertj
93 * Removed clearing of lock callbacks in context destructor, should not!
95 * Revision 1.18 2001/02/16 07:13:41 robertj
96 * Fixed bug in PSSLChannel error detection, thinks a zero byte write is error.
98 * Revision 1.17 2000/11/27 06:46:16 robertj
99 * Added asserts with SSL error message text.
101 * Revision 1.16 2000/11/14 08:33:16 robertj
102 * Added certificate and private key classes.
104 * Revision 1.15 2000/11/03 10:00:43 robertj
105 * Fixed initialisation of SSL, needed random number seed for some modes.
107 * Revision 1.14 2000/09/01 03:32:46 robertj
108 * Fixed assert on setting directories for CAs.
110 * Revision 1.13 2000/09/01 02:06:00 craigs
111 * Changed to OpenSSL_add_ssl_algorthms to fix link problem on some machines
113 * Revision 1.12 2000/08/25 13:56:46 robertj
114 * Fixed some GNU warnings
116 * Revision 1.11 2000/08/25 08:11:02 robertj
117 * Fixed OpenSSL support so can operate as a server channel.
119 * Revision 1.10 2000/08/04 12:52:18 robertj
120 * SSL changes, added error functions, removed need to have openssl include directory in app.
122 * Revision 1.9 2000/01/10 02:24:09 craigs
123 * Updated for new OpenSSL
125 * Revision 1.8 1998/12/04 13:04:18 craigs
126 * Changed for SSLeay 0.9
128 * Revision 1.7 1998/09/23 06:22:35 robertj
129 * Added open source copyright license.
131 * Revision 1.6 1998/01/26 02:50:17 robertj
132 * GNU Support
134 * Revision 1.5 1997/05/04 02:50:54 craigs
135 * Added support for client and server sertificates
137 * Revision 1.1 1996/11/15 07:38:34 craigs
138 * Initial revision
142 /* crypto/buffer/bss_sock.c */
143 /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au)
144 * All rights reserved.
146 * This file is part of an SSL implementation written
147 * by Eric Young (eay@mincom.oz.au).
148 * The implementation was written so as to conform with Netscapes SSL
149 * specification. This library and applications are
150 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
151 * as long as the following conditions are aheared to.
153 * Copyright remains Eric Young's, and as such any Copyright notices in
154 * the code are not to be removed. If this code is used in a product,
155 * Eric Young should be given attribution as the author of the parts used.
156 * This can be in the form of a textual message at program startup or
157 * in documentation (online or textual) provided with the package.
159 * Redistribution and use in source and binary forms, with or without
160 * modification, are permitted provided that the following conditions
161 * are met:
162 * 1. Redistributions of source code must retain the copyright
163 * notice, this list of conditions and the following disclaimer.
164 * 2. Redistributions in binary form must reproduce the above copyright
165 * notice, this list of conditions and the following disclaimer in the
166 * documentation and/or other materials provided with the distribution.
167 * 3. All advertising materials mentioning features or use of this software
168 * must display the following acknowledgement:
169 * This product includes software developed by Eric Young (eay@mincom.oz.au)
171 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
172 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
175 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
176 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
177 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
178 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
179 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
180 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
181 * SUCH DAMAGE.
183 * The licence and distribution terms for any publically available version or
184 * derivative of this code cannot be changed. i.e. this code cannot simply be
185 * copied and put under another distribution licence
186 * [including the GNU Public Licence.]
189 #ifdef __GNUC__
190 #pragma implementation "pssl.h"
191 #endif
193 #include <ptlib.h>
195 #include <ptclib/pssl.h>
196 #include <ptclib/mime.h>
198 #if P_SSL
200 #define USE_SOCKETS
202 extern "C" {
204 #include <openssl/ssl.h>
205 #include <openssl/err.h>
206 #include <openssl/rand.h>
211 #ifdef _MSC_VER
213 #pragma comment(lib, P_SSL_LIB1)
214 #pragma comment(lib, P_SSL_LIB2)
215 #pragma comment(linker, "/delayload:ssleay32.dll")
216 #pragma comment(linker, "/delayload:libeay32.dll")
217 #pragma comment(lib, "Delayimp.lib")
219 #endif
222 ///////////////////////////////////////////////////////////////////////////////
224 PARRAY(PSSLMutexArrayBase, PMutex);
225 class PSSLMutexArray : public PSSLMutexArrayBase
227 PCLASSINFO(PSSLMutexArray, PSSLMutexArrayBase);
228 public:
229 PSSLMutexArray();
233 class PSSL_BIO
235 public:
236 PSSL_BIO(BIO_METHOD *method = BIO_s_file_internal())
237 { bio = BIO_new(method); }
239 ~PSSL_BIO()
240 { BIO_free(bio); }
242 operator BIO*() const
243 { return bio; }
245 bool OpenRead(const PFilePath & filename)
246 { return BIO_read_filename(bio, (char *)(const char *)filename) > 0; }
248 bool OpenWrite(const PFilePath & filename)
249 { return BIO_write_filename(bio, (char *)(const char *)filename) > 0; }
251 bool OpenAppend(const PFilePath & filename)
252 { return BIO_append_filename(bio, (char *)(const char *)filename) > 0; }
254 protected:
255 BIO * bio;
259 #define new PNEW
262 ///////////////////////////////////////////////////////////////////////////////
264 PSSLMutexArray::PSSLMutexArray()
266 // Initialise all of the mutexes for multithreaded operation.
267 SetSize(CRYPTO_num_locks());
268 for (PINDEX i = 0; i < GetSize(); i++)
269 SetAt(i, new PMutex);
273 ///////////////////////////////////////////////////////////////////////////////
275 PSSLPrivateKey::PSSLPrivateKey()
277 key = NULL;
281 PSSLPrivateKey::PSSLPrivateKey(unsigned modulus,
282 void (*callback)(int,int,void *),
283 void *cb_arg)
285 key = NULL;
286 Create(modulus, callback, cb_arg);
290 PSSLPrivateKey::PSSLPrivateKey(const PFilePath & keyFile, PSSLFileTypes fileType)
292 key = NULL;
293 Load(keyFile, fileType);
297 PSSLPrivateKey::PSSLPrivateKey(const BYTE * keyData, PINDEX keySize)
299 key = d2i_AutoPrivateKey(NULL, (BYTE **)&keyData, keySize);
303 PSSLPrivateKey::PSSLPrivateKey(const PBYTEArray & keyData)
305 const BYTE * keyPtr = keyData;
306 key = d2i_AutoPrivateKey(NULL, (BYTE **)&keyPtr, keyData.GetSize());
310 PSSLPrivateKey::PSSLPrivateKey(const PSSLPrivateKey & privKey)
312 key = privKey.key;
316 PSSLPrivateKey & PSSLPrivateKey::operator=(const PSSLPrivateKey & privKey)
318 if (key != NULL)
319 EVP_PKEY_free(key);
321 key = privKey.key;
323 return *this;
327 PSSLPrivateKey::~PSSLPrivateKey()
329 if (key != NULL)
330 EVP_PKEY_free(key);
334 BOOL PSSLPrivateKey::Create(unsigned modulus,
335 void (*callback)(int,int,void *),
336 void *cb_arg)
338 if (key != NULL) {
339 EVP_PKEY_free(key);
340 key = NULL;
343 if (modulus < 384) {
344 return FALSE;
347 key = EVP_PKEY_new();
348 if (key == NULL)
349 return FALSE;
351 if (EVP_PKEY_assign_RSA(key, RSA_generate_key(modulus, 0x10001, callback, cb_arg)))
352 return TRUE;
354 EVP_PKEY_free(key);
355 key = NULL;
356 return FALSE;
360 PBYTEArray PSSLPrivateKey::GetData() const
362 PBYTEArray data;
364 if (key != NULL) {
365 BYTE * keyPtr = data.GetPointer(i2d_PrivateKey(key, NULL));
366 i2d_PrivateKey(key, &keyPtr);
369 return data;
373 PString PSSLPrivateKey::AsString() const
375 return PBase64::Encode(GetData());
379 BOOL PSSLPrivateKey::Load(const PFilePath & keyFile, PSSLFileTypes fileType)
381 if (key != NULL) {
382 EVP_PKEY_free(key);
383 key = NULL;
386 PSSL_BIO in;
387 if (!in.OpenRead(keyFile)) {
388 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
389 return FALSE;
392 if (fileType == PSSLFileTypeDEFAULT)
393 fileType = keyFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
395 switch (fileType) {
396 case PSSLFileTypeASN1 :
397 key = d2i_PrivateKey_bio(in, NULL);
398 if (key != NULL)
399 return TRUE;
401 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
402 break;
404 case PSSLFileTypePEM :
405 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
406 if (key != NULL)
407 return TRUE;
409 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
410 break;
412 default :
413 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
416 return FALSE;
420 BOOL PSSLPrivateKey::Save(const PFilePath & keyFile, BOOL append, PSSLFileTypes fileType)
422 if (key == NULL)
423 return FALSE;
425 PSSL_BIO out;
426 if (!(append ? out.OpenAppend(keyFile) : out.OpenWrite(keyFile))) {
427 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
428 return FALSE;
431 if (fileType == PSSLFileTypeDEFAULT)
432 fileType = keyFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
434 switch (fileType) {
435 case PSSLFileTypeASN1 :
436 if (i2d_PrivateKey_bio(out, key))
437 return TRUE;
439 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
440 break;
442 case PSSLFileTypePEM :
443 if (PEM_write_bio_PrivateKey(out, key, NULL, NULL, 0, 0, NULL))
444 return TRUE;
446 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
447 break;
449 default :
450 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
453 return FALSE;
457 ///////////////////////////////////////////////////////////////////////////////
459 PSSLCertificate::PSSLCertificate()
461 certificate = NULL;
465 PSSLCertificate::PSSLCertificate(const PFilePath & certFile, PSSLFileTypes fileType)
467 certificate = NULL;
468 Load(certFile, fileType);
472 PSSLCertificate::PSSLCertificate(const BYTE * certData, PINDEX certSize)
474 certificate = d2i_X509(NULL, (unsigned char **)&certData, certSize);
478 PSSLCertificate::PSSLCertificate(const PBYTEArray & certData)
480 const BYTE * certPtr = certData;
481 certificate = d2i_X509(NULL, (unsigned char **)&certPtr, certData.GetSize());
485 PSSLCertificate::PSSLCertificate(const PString & certStr)
487 PBYTEArray certData;
488 PBase64::Decode(certStr, certData);
489 if (certData.GetSize() > 0) {
490 const BYTE * certPtr = certData;
491 certificate = d2i_X509(NULL, (unsigned char **)&certPtr, certData.GetSize());
493 else
494 certificate = NULL;
498 PSSLCertificate::PSSLCertificate(const PSSLCertificate & cert)
500 if (cert.certificate == NULL)
501 certificate = NULL;
502 else
503 certificate = X509_dup(cert.certificate);
507 PSSLCertificate & PSSLCertificate::operator=(const PSSLCertificate & cert)
509 if (certificate != NULL)
510 X509_free(certificate);
511 if (cert.certificate == NULL)
512 certificate = NULL;
513 else
514 certificate = X509_dup(cert.certificate);
516 return *this;
520 PSSLCertificate::~PSSLCertificate()
522 if (certificate != NULL)
523 X509_free(certificate);
527 BOOL PSSLCertificate::CreateRoot(const PString & subject,
528 const PSSLPrivateKey & privateKey)
530 if (certificate != NULL) {
531 X509_free(certificate);
532 certificate = NULL;
535 if (privateKey == NULL)
536 return FALSE;
538 POrdinalToString info;
539 PStringArray fields = subject.Tokenise('/', FALSE);
540 PINDEX i;
541 for (i = 0; i < fields.GetSize(); i++) {
542 PString field = fields[i];
543 PINDEX equals = field.Find('=');
544 if (equals != P_MAX_INDEX) {
545 int nid = OBJ_txt2nid((char *)(const char *)field.Left(equals));
546 if (nid != NID_undef)
547 info.SetAt(nid, field.Mid(equals+1));
550 if (info.IsEmpty())
551 return FALSE;
553 certificate = X509_new();
554 if (certificate == NULL)
555 return FALSE;
557 if (X509_set_version(certificate, 2)) {
558 /* Set version to V3 */
559 ASN1_INTEGER_set(X509_get_serialNumber(certificate), 0L);
561 X509_NAME * name = X509_NAME_new();
562 for (i = 0; i < info.GetSize(); i++)
563 X509_NAME_add_entry_by_NID(name,
564 info.GetKeyAt(i),
565 MBSTRING_ASC,
566 (unsigned char *)(const char *)info.GetDataAt(i),
567 -1,-1, 0);
568 X509_set_issuer_name(certificate, name);
569 X509_set_subject_name(certificate, name);
570 X509_NAME_free(name);
572 X509_gmtime_adj(X509_get_notBefore(certificate), 0);
573 X509_gmtime_adj(X509_get_notAfter(certificate), (long)60*60*24*365*5);
575 X509_PUBKEY * pubkey = X509_PUBKEY_new();
576 if (pubkey != NULL) {
577 X509_PUBKEY_set(&pubkey, privateKey);
578 EVP_PKEY * pkey = X509_PUBKEY_get(pubkey);
579 X509_set_pubkey(certificate, pkey);
580 EVP_PKEY_free(pkey);
581 X509_PUBKEY_free(pubkey);
583 if (X509_sign(certificate, privateKey, EVP_md5()) > 0)
584 return TRUE;
588 X509_free(certificate);
589 certificate = NULL;
590 return FALSE;
594 PBYTEArray PSSLCertificate::GetData() const
596 PBYTEArray data;
598 if (certificate != NULL) {
599 BYTE * certPtr = data.GetPointer(i2d_X509(certificate, NULL));
600 i2d_X509(certificate, &certPtr);
603 return data;
607 PString PSSLCertificate::AsString() const
609 return PBase64::Encode(GetData());
613 BOOL PSSLCertificate::Load(const PFilePath & certFile, PSSLFileTypes fileType)
615 if (certificate != NULL) {
616 X509_free(certificate);
617 certificate = NULL;
620 PSSL_BIO in;
621 if (!in.OpenRead(certFile)) {
622 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
623 return FALSE;
626 if (fileType == PSSLFileTypeDEFAULT)
627 fileType = certFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
629 switch (fileType) {
630 case PSSLFileTypeASN1 :
631 certificate = d2i_X509_bio(in, NULL);
632 if (certificate != NULL)
633 return TRUE;
635 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
636 break;
638 case PSSLFileTypePEM :
639 certificate = PEM_read_bio_X509(in, NULL, NULL, NULL);
640 if (certificate != NULL)
641 return TRUE;
643 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
644 break;
646 default :
647 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
650 return FALSE;
654 BOOL PSSLCertificate::Save(const PFilePath & certFile, BOOL append, PSSLFileTypes fileType)
656 if (certificate == NULL)
657 return FALSE;
659 PSSL_BIO out;
660 if (!(append ? out.OpenAppend(certFile) : out.OpenWrite(certFile))) {
661 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
662 return FALSE;
665 if (fileType == PSSLFileTypeDEFAULT)
666 fileType = certFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
668 switch (fileType) {
669 case PSSLFileTypeASN1 :
670 if (i2d_X509_bio(out, certificate))
671 return TRUE;
673 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
674 break;
676 case PSSLFileTypePEM :
677 if (PEM_write_bio_X509(out, certificate))
678 return TRUE;
680 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
681 break;
683 default :
684 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
687 return FALSE;
691 ///////////////////////////////////////////////////////////////////////////////
693 PSSLDiffieHellman::PSSLDiffieHellman()
695 dh = NULL;
699 PSSLDiffieHellman::PSSLDiffieHellman(const PFilePath & dhFile,
700 PSSLFileTypes fileType)
702 dh = NULL;
703 Load(dhFile, fileType);
707 PSSLDiffieHellman::PSSLDiffieHellman(const BYTE * pData, PINDEX pSize,
708 const BYTE * gData, PINDEX gSize)
710 dh = DH_new();
711 if (dh == NULL)
712 return;
714 dh->p = BN_bin2bn(pData, pSize, NULL);
715 dh->g = BN_bin2bn(gData, gSize, NULL);
716 if (dh->p != NULL && dh->g != NULL)
717 return;
719 DH_free(dh);
720 dh = NULL;
724 PSSLDiffieHellman::PSSLDiffieHellman(const PSSLDiffieHellman & diffie)
726 dh = diffie.dh;
730 PSSLDiffieHellman & PSSLDiffieHellman::operator=(const PSSLDiffieHellman & diffie)
732 if (dh != NULL)
733 DH_free(dh);
734 dh = diffie.dh;
735 return *this;
739 PSSLDiffieHellman::~PSSLDiffieHellman()
741 if (dh != NULL)
742 DH_free(dh);
745 #ifdef __BEOS__
746 // 2/21/04 Yuri Kiryanov - fix for compiler choke on BeOS for usage of
747 // SSL function d2i_DHparams_bio below in PSSLDiffieHellman::Load
748 #undef d2i_DHparams_bio
749 #define d2i_DHparams_bio(bp,x) \
750 (DH *)ASN1_d2i_bio( \
751 (char *(*)(...))(void *)DH_new, \
752 (char *(*)(...))(void *)d2i_DHparams, \
753 (bp), \
754 (unsigned char **)(x) \
756 #endif
758 BOOL PSSLDiffieHellman::Load(const PFilePath & dhFile,
759 PSSLFileTypes fileType)
761 if (dh != NULL) {
762 DH_free(dh);
763 dh = NULL;
766 PSSL_BIO in;
767 if (!in.OpenRead(dhFile)) {
768 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
769 return FALSE;
772 if (fileType == PSSLFileTypeDEFAULT)
773 fileType = dhFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
775 switch (fileType) {
776 case PSSLFileTypeASN1 :
777 dh = d2i_DHparams_bio(in, NULL);
778 if (dh != NULL)
779 return TRUE;
781 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
782 break;
784 case PSSLFileTypePEM :
785 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
786 if (dh != NULL)
787 return TRUE;
789 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
790 break;
792 default :
793 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
796 return FALSE;
800 ///////////////////////////////////////////////////////////////////////////////
802 static void LockingCallback(int mode, int n, const char * /*file*/, int /*line*/)
804 static PSSLMutexArray mutexes;
805 if ((mode & CRYPTO_LOCK) != 0)
806 mutexes[n].Wait();
807 else
808 mutexes[n].Signal();
812 static int VerifyCallBack(int ok, X509_STORE_CTX * ctx)
814 X509 * err_cert = X509_STORE_CTX_get_current_cert(ctx);
815 //int err = X509_STORE_CTX_get_error(ctx);
817 // get the subject name, just for verification
818 char buf[256];
819 X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
821 PTRACE(1, "SSL\tVerify callback depth "
822 << X509_STORE_CTX_get_error_depth(ctx)
823 << " : cert name = " << buf);
825 return ok;
829 static void PSSLAssert(const char * msg)
831 char buf[256];
832 strcpy(buf, msg);
833 ERR_error_string(ERR_peek_error(), &buf[strlen(msg)]);
834 PTRACE(1, "SSL\t" << buf);
835 PAssertAlways(buf);
839 PSSLContext::PSSLContext(const void * sessionId, PINDEX idSize)
841 static PMutex InitialisationMutex;
842 InitialisationMutex.Wait();
844 static BOOL needInitialisation = TRUE;
845 if (needInitialisation) {
846 SSL_load_error_strings();
847 OpenSSL_add_ssl_algorithms();
849 // Seed the random number generator
850 BYTE seed[128];
851 for (size_t i = 0; i < sizeof(seed); i++)
852 seed[i] = (BYTE)rand();
853 RAND_seed(seed, sizeof(seed));
855 // set up multithread stuff
856 CRYPTO_set_locking_callback(LockingCallback);
858 needInitialisation = FALSE;
861 InitialisationMutex.Signal();
863 // create the new SSL context
864 SSL_METHOD * meth = SSLv23_method();
865 context = SSL_CTX_new(meth);
866 if (context == NULL)
867 PSSLAssert("Error creating context: ");
869 // Shutdown
870 SSL_CTX_set_quiet_shutdown(context, 1);
872 // Set default locations
873 if (!SSL_CTX_load_verify_locations(context, NULL, ".") ||
874 !SSL_CTX_set_default_verify_paths(context))
875 PSSLAssert("Cannot set CAfile and path: ");
877 if (sessionId != NULL) {
878 if (idSize == 0)
879 idSize = ::strlen((const char *)sessionId)+1;
880 SSL_CTX_set_session_id_context(context, (const BYTE *)sessionId, idSize);
881 SSL_CTX_sess_set_cache_size(context, 128);
884 // set default verify mode
885 SSL_CTX_set_verify(context, SSL_VERIFY_NONE, VerifyCallBack);
889 PSSLContext::~PSSLContext()
891 SSL_CTX_free(context);
895 BOOL PSSLContext::SetCAPath(const PDirectory & caPath)
897 PString path = caPath.Left(caPath.GetLength()-1);
898 if (!SSL_CTX_load_verify_locations(context, NULL, path))
899 return FALSE;
901 return SSL_CTX_set_default_verify_paths(context);
905 BOOL PSSLContext::SetCAFile(const PFilePath & caFile)
907 if (!SSL_CTX_load_verify_locations(context, caFile, NULL))
908 return FALSE;
910 return SSL_CTX_set_default_verify_paths(context);
914 BOOL PSSLContext::UseCertificate(const PSSLCertificate & certificate)
916 return SSL_CTX_use_certificate(context, certificate) > 0;
920 BOOL PSSLContext::UsePrivateKey(const PSSLPrivateKey & key)
922 if (SSL_CTX_use_PrivateKey(context, key) <= 0)
923 return FALSE;
925 return SSL_CTX_check_private_key(context);
929 BOOL PSSLContext::UseDiffieHellman(const PSSLDiffieHellman & dh)
931 return SSL_CTX_set_tmp_dh(context, (dh_st *)dh) > 0;
935 BOOL PSSLContext::SetCipherList(const PString & ciphers)
937 if (ciphers.IsEmpty())
938 return FALSE;
940 return SSL_CTX_set_cipher_list(context, (char *)(const char *)ciphers);
944 /////////////////////////////////////////////////////////////////////////
946 // SSLChannel
949 PSSLChannel::PSSLChannel(PSSLContext * ctx, BOOL autoDel)
951 if (ctx != NULL) {
952 context = ctx;
953 autoDeleteContext = autoDel;
955 else {
956 context = new PSSLContext;
957 autoDeleteContext = TRUE;
960 ssl = SSL_new(*context);
961 if (ssl == NULL)
962 PSSLAssert("Error creating channel: ");
966 PSSLChannel::PSSLChannel(PSSLContext & ctx)
968 context = &ctx;
969 autoDeleteContext = FALSE;
971 ssl = SSL_new(*context);
975 PSSLChannel::~PSSLChannel()
977 // free the SSL connection
978 if (ssl != NULL)
979 SSL_free(ssl);
981 if (autoDeleteContext)
982 delete context;
986 BOOL PSSLChannel::Read(void * buf, PINDEX len)
988 flush();
990 channelPointerMutex.StartRead();
992 lastReadCount = 0;
994 BOOL returnValue = FALSE;
995 if (readChannel == NULL)
996 SetErrorValues(NotOpen, EBADF, LastReadError);
997 else if (readTimeout == 0 && SSL_pending(ssl) == 0)
998 SetErrorValues(Timeout, ETIMEDOUT, LastReadError);
999 else {
1000 readChannel->SetReadTimeout(readTimeout);
1002 int readResult = SSL_read(ssl, (char *)buf, len);
1003 lastReadCount = readResult;
1004 returnValue = readResult > 0;
1005 if (readResult < 0 && GetErrorCode(LastReadError) == NoError)
1006 ConvertOSError(-1, LastReadError);
1009 channelPointerMutex.EndRead();
1011 return returnValue;
1014 BOOL PSSLChannel::Write(const void * buf, PINDEX len)
1016 flush();
1018 channelPointerMutex.StartRead();
1020 lastWriteCount = 0;
1022 BOOL returnValue;
1023 if (writeChannel == NULL) {
1024 SetErrorValues(NotOpen, EBADF, LastWriteError);
1025 returnValue = FALSE;
1027 else {
1028 writeChannel->SetWriteTimeout(writeTimeout);
1030 int writeResult = SSL_write(ssl, (const char *)buf, len);
1031 lastWriteCount = writeResult;
1032 returnValue = lastWriteCount >= len;
1033 if (writeResult < 0 && GetErrorCode(LastWriteError) == NoError)
1034 ConvertOSError(-1, LastWriteError);
1037 channelPointerMutex.EndRead();
1039 return returnValue;
1043 BOOL PSSLChannel::Close()
1045 BOOL ok = SSL_shutdown(ssl);
1046 return PIndirectChannel::Close() && ok;
1050 BOOL PSSLChannel::ConvertOSError(int error, ErrorGroup group)
1052 Errors lastError = NoError;
1053 DWORD osError = 0;
1054 if (SSL_get_error(ssl, error) != SSL_ERROR_NONE && (osError = ERR_peek_error()) != 0) {
1055 osError |= 0x80000000;
1056 lastError = Miscellaneous;
1059 return SetErrorValues(lastError, osError, group);
1063 PString PSSLChannel::GetErrorText(ErrorGroup group) const
1065 if ((lastErrorNumber[group]&0x80000000) == 0)
1066 return PIndirectChannel::GetErrorText(group);
1068 char buf[200];
1069 return ERR_error_string(lastErrorNumber[group]&0x7fffffff, buf);
1073 BOOL PSSLChannel::Accept()
1075 if (IsOpen())
1076 return ConvertOSError(SSL_accept(ssl));
1077 return FALSE;
1081 BOOL PSSLChannel::Accept(PChannel & channel)
1083 if (Open(channel))
1084 return ConvertOSError(SSL_accept(ssl));
1085 return FALSE;
1089 BOOL PSSLChannel::Accept(PChannel * channel, BOOL autoDelete)
1091 if (Open(channel, autoDelete))
1092 return ConvertOSError(SSL_accept(ssl));
1093 return FALSE;
1097 BOOL PSSLChannel::Connect()
1099 if (IsOpen())
1100 return ConvertOSError(SSL_connect(ssl));
1101 return FALSE;
1105 BOOL PSSLChannel::Connect(PChannel & channel)
1107 if (Open(channel))
1108 return ConvertOSError(SSL_connect(ssl));
1109 return FALSE;
1113 BOOL PSSLChannel::Connect(PChannel * channel, BOOL autoDelete)
1115 if (Open(channel, autoDelete))
1116 return ConvertOSError(SSL_connect(ssl));
1117 return FALSE;
1121 BOOL PSSLChannel::UseCertificate(const PSSLCertificate & certificate)
1123 return SSL_use_certificate(ssl, certificate);
1127 void PSSLChannel::SetVerifyMode(VerifyMode mode)
1129 int verify;
1131 switch (mode) {
1132 default :
1133 case VerifyNone:
1134 verify = SSL_VERIFY_NONE;
1135 break;
1137 case VerifyPeer:
1138 verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
1139 break;
1141 case VerifyPeerMandatory:
1142 verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1145 SSL_set_verify(ssl, verify, VerifyCallBack);
1149 BOOL PSSLChannel::RawSSLRead(void * buf, PINDEX & len)
1151 if (!PIndirectChannel::Read(buf, len))
1152 return FALSE;
1154 len = GetLastReadCount();
1155 return TRUE;
1159 //////////////////////////////////////////////////////////////////////////
1161 // Low level interface to SSLEay routines
1165 #define PSSLCHANNEL(bio) ((PSSLChannel *)(bio->ptr))
1167 extern "C" {
1169 #if (OPENSSL_VERSION_NUMBER < 0x00906000)
1171 typedef int (*ifptr)();
1172 typedef long (*lfptr)();
1174 #endif
1176 static int Psock_new(BIO * bio)
1178 bio->init = 0;
1179 bio->num = 0;
1180 bio->ptr = NULL; // this is really (PSSLChannel *)
1181 bio->flags = 0;
1183 return(1);
1187 static int Psock_free(BIO * bio)
1189 if (bio == NULL)
1190 return 0;
1192 if (bio->shutdown) {
1193 if (bio->init) {
1194 PSSLCHANNEL(bio)->Shutdown(PSocket::ShutdownReadAndWrite);
1195 PSSLCHANNEL(bio)->Close();
1197 bio->init = 0;
1198 bio->flags = 0;
1200 return 1;
1204 static long Psock_ctrl(BIO * bio, int cmd, long num, void * /*ptr*/)
1206 switch (cmd) {
1207 case BIO_CTRL_SET_CLOSE:
1208 bio->shutdown = (int)num;
1209 return 1;
1211 case BIO_CTRL_GET_CLOSE:
1212 return bio->shutdown;
1214 case BIO_CTRL_FLUSH:
1215 return 1;
1218 // Other BIO commands, return 0
1219 return 0;
1223 static int Psock_read(BIO * bio, char * out, int outl)
1225 if (out == NULL)
1226 return 0;
1228 BIO_clear_retry_flags(bio);
1230 // Skip over the polymorphic read, want to do real one
1231 PINDEX len = outl;
1232 if (PSSLCHANNEL(bio)->RawSSLRead(out, len))
1233 return len;
1235 switch (PSSLCHANNEL(bio)->GetErrorCode(PChannel::LastReadError)) {
1236 case PChannel::Interrupted :
1237 case PChannel::Timeout :
1238 BIO_set_retry_read(bio);
1239 return -1;
1241 default :
1242 break;
1245 return 0;
1249 static int Psock_write(BIO * bio, const char * in, int inl)
1251 if (in == NULL)
1252 return 0;
1254 BIO_clear_retry_flags(bio);
1256 // Skip over the polymorphic write, want to do real one
1257 if (PSSLCHANNEL(bio)->PIndirectChannel::Write(in, inl))
1258 return PSSLCHANNEL(bio)->GetLastWriteCount();
1260 switch (PSSLCHANNEL(bio)->GetErrorCode(PChannel::LastWriteError)) {
1261 case PChannel::Interrupted :
1262 case PChannel::Timeout :
1263 BIO_set_retry_write(bio);
1264 return -1;
1266 default :
1267 break;
1270 return 0;
1274 static int Psock_puts(BIO * bio, const char * str)
1276 int n,ret;
1278 n = strlen(str);
1279 ret = Psock_write(bio,str,n);
1281 return ret;
1287 static BIO_METHOD methods_Psock =
1289 BIO_TYPE_SOCKET,
1290 "PTLib-PSSLChannel",
1291 #if (OPENSSL_VERSION_NUMBER < 0x00906000)
1292 (ifptr)Psock_write,
1293 (ifptr)Psock_read,
1294 (ifptr)Psock_puts,
1295 NULL,
1296 (lfptr)Psock_ctrl,
1297 (ifptr)Psock_new,
1298 (ifptr)Psock_free
1299 #else
1300 Psock_write,
1301 Psock_read,
1302 Psock_puts,
1303 NULL,
1304 Psock_ctrl,
1305 Psock_new,
1306 Psock_free
1307 #endif
1311 BOOL PSSLChannel::OnOpen()
1313 BIO * bio = BIO_new(&methods_Psock);
1314 if (bio == NULL) {
1315 SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
1316 return FALSE;
1319 // "Open" then bio
1320 bio->ptr = this;
1321 bio->init = 1;
1323 SSL_set_bio(ssl, bio, bio);
1324 return TRUE;
1328 //////////////////////////////////////////////////////////////////////////
1330 // misc unused code
1333 #if 0
1335 extern "C" {
1337 static verify_depth = 0;
1338 static verify_error = VERIFY_OK;
1340 // should be X509 * but we can just have them as char *.
1341 int verify_callback(int ok, X509 * xs, X509 * xi, int depth, int error)
1343 char *s;
1345 s = (char *)X509_NAME_oneline(X509_get_subject_name(xs));
1346 if (s == NULL) {
1347 // ERR_print_errors(bio_err);
1348 return(0);
1350 PError << "depth= " << depth << " " << (char *)s << endl;
1351 free(s);
1352 if (error == VERIFY_ERR_UNABLE_TO_GET_ISSUER) {
1353 s=(char *)X509_NAME_oneline(X509_get_issuer_name(xs));
1354 if (s == NULL) {
1355 PError << "verify error" << endl;
1356 //ERR_print_errors(bio_err);
1357 return(0);
1359 PError << "issuer = " << s << endl;
1360 free(s);
1363 if (!ok) {
1364 PError << "verify error:num=" << error << " " <<
1365 X509_cert_verify_error_string(error) << endl;
1366 if (verify_depth <= depth) {
1367 ok=1;
1368 verify_error=VERIFY_OK;
1369 } else {
1370 ok=0;
1371 verify_error=error;
1374 PError << "verify return:" << ok << endl;
1375 return(ok);
1380 #endif
1382 #endif // P_SSL
1385 // End of file ////////////////////////////////////////////////////////////////