Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / pssl.cxx
blobf4178787e1ece767f892d8ea379e0c738d18fa7d
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.37 2004/02/22 01:57:37 ykiryanov
32 * Put a fix for a compiler choke on BeOS when calling macro d2i_DHparams_bio in PSSLDiffieHellman::Load. Fast on Monday.
34 * Revision 1.36 2003/04/16 08:00:19 robertj
35 * Windoes psuedo autoconf support
37 * Revision 1.35 2002/11/06 22:47:25 robertj
38 * Fixed header comment (copyright etc)
40 * Revision 1.34 2002/06/07 02:55:23 robertj
41 * Fixed GNU warning
43 * Revision 1.33 2002/04/02 16:59:35 robertj
44 * Fixed GNU warning
46 * Revision 1.32 2002/04/02 16:17:28 robertj
47 * Fixed bug where returned TRUE when read count 0 or write count not len
48 * which is not according to Read()/Write() semantics. Could cause high CPU
49 * loops when sockets shutdown.
51 * Revision 1.31 2002/03/28 07:26:25 robertj
52 * Added Diffie-Hellman parameters wrapper class.
54 * Revision 1.30 2001/12/13 09:15:41 robertj
55 * Added function to get private key as ray DER binary data or as base64 string.
57 * Revision 1.29 2001/12/06 04:06:03 robertj
58 * Removed "Win32 SSL xxx" build configurations in favour of system
59 * environment variables to select optional libraries.
61 * Revision 1.28 2001/12/04 02:59:18 robertj
62 * Fixed problem on platforms where a pointer is not the same size as an int.
64 * Revision 1.27 2001/10/31 01:31:26 robertj
65 * Added enhancements for saving/loading/creating certificates and keys.
67 * Revision 1.26 2001/09/28 08:50:48 robertj
68 * Fixed bug where last count not set to zero if have error on read/write.
70 * Revision 1.25 2001/09/10 02:51:23 robertj
71 * Major change to fix problem with error codes being corrupted in a
72 * PChannel when have simultaneous reads and writes in threads.
74 * Revision 1.24 2001/06/01 00:53:59 robertj
75 * Added certificate constructor that takes a PBYTEArray
77 * Revision 1.23 2001/05/31 07:04:11 craigs
78 * Changed random seed function
80 * Revision 1.22 2001/05/29 03:33:54 craigs
81 * Added options to be compatible with OpenSSL 0.9.6
83 * Revision 1.21 2001/05/16 06:31:37 robertj
84 * Fixed GNU C++ compatibility
86 * Revision 1.20 2001/05/16 06:02:37 craigs
87 * Changed to allow detection of non-SSL connection to SecureHTTPServiceProcess
89 * Revision 1.19 2001/05/09 07:00:22 robertj
90 * Removed clearing of lock callbacks in context destructor, should not!
92 * Revision 1.18 2001/02/16 07:13:41 robertj
93 * Fixed bug in PSSLChannel error detection, thinks a zero byte write is error.
95 * Revision 1.17 2000/11/27 06:46:16 robertj
96 * Added asserts with SSL error message text.
98 * Revision 1.16 2000/11/14 08:33:16 robertj
99 * Added certificate and private key classes.
101 * Revision 1.15 2000/11/03 10:00:43 robertj
102 * Fixed initialisation of SSL, needed random number seed for some modes.
104 * Revision 1.14 2000/09/01 03:32:46 robertj
105 * Fixed assert on setting directories for CAs.
107 * Revision 1.13 2000/09/01 02:06:00 craigs
108 * Changed to OpenSSL_add_ssl_algorthms to fix link problem on some machines
110 * Revision 1.12 2000/08/25 13:56:46 robertj
111 * Fixed some GNU warnings
113 * Revision 1.11 2000/08/25 08:11:02 robertj
114 * Fixed OpenSSL support so can operate as a server channel.
116 * Revision 1.10 2000/08/04 12:52:18 robertj
117 * SSL changes, added error functions, removed need to have openssl include directory in app.
119 * Revision 1.9 2000/01/10 02:24:09 craigs
120 * Updated for new OpenSSL
122 * Revision 1.8 1998/12/04 13:04:18 craigs
123 * Changed for SSLeay 0.9
125 * Revision 1.7 1998/09/23 06:22:35 robertj
126 * Added open source copyright license.
128 * Revision 1.6 1998/01/26 02:50:17 robertj
129 * GNU Support
131 * Revision 1.5 1997/05/04 02:50:54 craigs
132 * Added support for client and server sertificates
134 * Revision 1.1 1996/11/15 07:38:34 craigs
135 * Initial revision
139 /* crypto/buffer/bss_sock.c */
140 /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au)
141 * All rights reserved.
143 * This file is part of an SSL implementation written
144 * by Eric Young (eay@mincom.oz.au).
145 * The implementation was written so as to conform with Netscapes SSL
146 * specification. This library and applications are
147 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
148 * as long as the following conditions are aheared to.
150 * Copyright remains Eric Young's, and as such any Copyright notices in
151 * the code are not to be removed. If this code is used in a product,
152 * Eric Young should be given attribution as the author of the parts used.
153 * This can be in the form of a textual message at program startup or
154 * in documentation (online or textual) provided with the package.
156 * Redistribution and use in source and binary forms, with or without
157 * modification, are permitted provided that the following conditions
158 * are met:
159 * 1. Redistributions of source code must retain the copyright
160 * notice, this list of conditions and the following disclaimer.
161 * 2. Redistributions in binary form must reproduce the above copyright
162 * notice, this list of conditions and the following disclaimer in the
163 * documentation and/or other materials provided with the distribution.
164 * 3. All advertising materials mentioning features or use of this software
165 * must display the following acknowledgement:
166 * This product includes software developed by Eric Young (eay@mincom.oz.au)
168 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
169 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
172 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
173 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
174 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
175 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
176 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
177 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
178 * SUCH DAMAGE.
180 * The licence and distribution terms for any publically available version or
181 * derivative of this code cannot be changed. i.e. this code cannot simply be
182 * copied and put under another distribution licence
183 * [including the GNU Public Licence.]
186 #ifdef __GNUC__
187 #pragma implementation "pssl.h"
188 #endif
190 #include <ptlib.h>
192 #include <ptclib/pssl.h>
193 #include <ptclib/mime.h>
195 #if P_SSL
197 #define USE_SOCKETS
199 extern "C" {
201 #include <openssl/ssl.h>
202 #include <openssl/err.h>
203 #include <openssl/rand.h>
208 #ifdef _MSC_VER
209 #pragma comment(lib, P_SSL_LIB1)
210 #pragma comment(lib, P_SSL_LIB2)
211 #endif
214 ///////////////////////////////////////////////////////////////////////////////
216 PARRAY(PSSLMutexArrayBase, PMutex);
217 class PSSLMutexArray : public PSSLMutexArrayBase
219 PCLASSINFO(PSSLMutexArray, PSSLMutexArrayBase);
220 public:
221 PSSLMutexArray();
225 class PSSL_BIO
227 public:
228 PSSL_BIO(BIO_METHOD *method = BIO_s_file_internal())
229 { bio = BIO_new(method); }
231 ~PSSL_BIO()
232 { BIO_free(bio); }
234 operator BIO*() const
235 { return bio; }
237 bool OpenRead(const PFilePath & filename)
238 { return BIO_read_filename(bio, (char *)(const char *)filename) > 0; }
240 bool OpenWrite(const PFilePath & filename)
241 { return BIO_write_filename(bio, (char *)(const char *)filename) > 0; }
243 bool OpenAppend(const PFilePath & filename)
244 { return BIO_append_filename(bio, (char *)(const char *)filename) > 0; }
246 protected:
247 BIO * bio;
251 #define new PNEW
254 ///////////////////////////////////////////////////////////////////////////////
256 PSSLMutexArray::PSSLMutexArray()
258 // Initialise all of the mutexes for multithreaded operation.
259 SetSize(CRYPTO_num_locks());
260 for (PINDEX i = 0; i < GetSize(); i++)
261 SetAt(i, new PMutex);
265 ///////////////////////////////////////////////////////////////////////////////
267 PSSLPrivateKey::PSSLPrivateKey()
269 key = NULL;
273 PSSLPrivateKey::PSSLPrivateKey(unsigned modulus,
274 void (*callback)(int,int,void *),
275 void *cb_arg)
277 key = NULL;
278 Create(modulus, callback, cb_arg);
282 PSSLPrivateKey::PSSLPrivateKey(const PFilePath & keyFile, PSSLFileTypes fileType)
284 key = NULL;
285 Load(keyFile, fileType);
289 PSSLPrivateKey::PSSLPrivateKey(const BYTE * keyData, PINDEX keySize)
291 key = d2i_AutoPrivateKey(NULL, (BYTE **)&keyData, keySize);
295 PSSLPrivateKey::PSSLPrivateKey(const PBYTEArray & keyData)
297 const BYTE * keyPtr = keyData;
298 key = d2i_AutoPrivateKey(NULL, (BYTE **)&keyPtr, keyData.GetSize());
302 PSSLPrivateKey::PSSLPrivateKey(const PSSLPrivateKey & privKey)
304 key = privKey.key;
308 PSSLPrivateKey & PSSLPrivateKey::operator=(const PSSLPrivateKey & privKey)
310 if (key != NULL)
311 EVP_PKEY_free(key);
313 key = privKey.key;
315 return *this;
319 PSSLPrivateKey::~PSSLPrivateKey()
321 if (key != NULL)
322 EVP_PKEY_free(key);
326 BOOL PSSLPrivateKey::Create(unsigned modulus,
327 void (*callback)(int,int,void *),
328 void *cb_arg)
330 if (key != NULL) {
331 EVP_PKEY_free(key);
332 key = NULL;
335 if (modulus < 384) {
336 return FALSE;
339 key = EVP_PKEY_new();
340 if (key == NULL)
341 return FALSE;
343 if (EVP_PKEY_assign_RSA(key, RSA_generate_key(modulus, 0x10001, callback, cb_arg)))
344 return TRUE;
346 EVP_PKEY_free(key);
347 key = NULL;
348 return FALSE;
352 PBYTEArray PSSLPrivateKey::GetData() const
354 PBYTEArray data;
356 if (key != NULL) {
357 BYTE * keyPtr = data.GetPointer(i2d_PrivateKey(key, NULL));
358 i2d_PrivateKey(key, &keyPtr);
361 return data;
365 PString PSSLPrivateKey::AsString() const
367 return PBase64::Encode(GetData());
371 BOOL PSSLPrivateKey::Load(const PFilePath & keyFile, PSSLFileTypes fileType)
373 if (key != NULL) {
374 EVP_PKEY_free(key);
375 key = NULL;
378 PSSL_BIO in;
379 if (!in.OpenRead(keyFile)) {
380 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
381 return FALSE;
384 if (fileType == PSSLFileTypeDEFAULT)
385 fileType = keyFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
387 switch (fileType) {
388 case PSSLFileTypeASN1 :
389 key = d2i_PrivateKey_bio(in, NULL);
390 if (key != NULL)
391 return TRUE;
393 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
394 break;
396 case PSSLFileTypePEM :
397 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
398 if (key != NULL)
399 return TRUE;
401 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
402 break;
404 default :
405 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
408 return FALSE;
412 BOOL PSSLPrivateKey::Save(const PFilePath & keyFile, BOOL append, PSSLFileTypes fileType)
414 if (key == NULL)
415 return FALSE;
417 PSSL_BIO out;
418 if (!(append ? out.OpenAppend(keyFile) : out.OpenWrite(keyFile))) {
419 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
420 return FALSE;
423 if (fileType == PSSLFileTypeDEFAULT)
424 fileType = keyFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
426 switch (fileType) {
427 case PSSLFileTypeASN1 :
428 if (i2d_PrivateKey_bio(out, key))
429 return TRUE;
431 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
432 break;
434 case PSSLFileTypePEM :
435 if (PEM_write_bio_PrivateKey(out, key, NULL, NULL, 0, 0, NULL))
436 return TRUE;
438 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
439 break;
441 default :
442 SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
445 return FALSE;
449 ///////////////////////////////////////////////////////////////////////////////
451 PSSLCertificate::PSSLCertificate()
453 certificate = NULL;
457 PSSLCertificate::PSSLCertificate(const PFilePath & certFile, PSSLFileTypes fileType)
459 certificate = NULL;
460 Load(certFile, fileType);
464 PSSLCertificate::PSSLCertificate(const BYTE * certData, PINDEX certSize)
466 certificate = d2i_X509(NULL, (unsigned char **)&certData, certSize);
470 PSSLCertificate::PSSLCertificate(const PBYTEArray & certData)
472 const BYTE * certPtr = certData;
473 certificate = d2i_X509(NULL, (unsigned char **)&certPtr, certData.GetSize());
477 PSSLCertificate::PSSLCertificate(const PString & certStr)
479 PBYTEArray certData;
480 PBase64::Decode(certStr, certData);
481 if (certData.GetSize() > 0) {
482 const BYTE * certPtr = certData;
483 certificate = d2i_X509(NULL, (unsigned char **)&certPtr, certData.GetSize());
485 else
486 certificate = NULL;
490 PSSLCertificate::PSSLCertificate(const PSSLCertificate & cert)
492 if (cert.certificate == NULL)
493 certificate = NULL;
494 else
495 certificate = X509_dup(cert.certificate);
499 PSSLCertificate & PSSLCertificate::operator=(const PSSLCertificate & cert)
501 if (certificate != NULL)
502 X509_free(certificate);
503 if (cert.certificate == NULL)
504 certificate = NULL;
505 else
506 certificate = X509_dup(cert.certificate);
508 return *this;
512 PSSLCertificate::~PSSLCertificate()
514 if (certificate != NULL)
515 X509_free(certificate);
519 BOOL PSSLCertificate::CreateRoot(const PString & subject,
520 const PSSLPrivateKey & privateKey)
522 if (certificate != NULL) {
523 X509_free(certificate);
524 certificate = NULL;
527 if (privateKey == NULL)
528 return FALSE;
530 POrdinalToString info;
531 PStringArray fields = subject.Tokenise('/', FALSE);
532 PINDEX i;
533 for (i = 0; i < fields.GetSize(); i++) {
534 PString field = fields[i];
535 PINDEX equals = field.Find('=');
536 if (equals != P_MAX_INDEX) {
537 int nid = OBJ_txt2nid((char *)(const char *)field.Left(equals));
538 if (nid != NID_undef)
539 info.SetAt(nid, field.Mid(equals+1));
542 if (info.IsEmpty())
543 return FALSE;
545 certificate = X509_new();
546 if (certificate == NULL)
547 return FALSE;
549 if (X509_set_version(certificate, 2)) {
550 /* Set version to V3 */
551 ASN1_INTEGER_set(X509_get_serialNumber(certificate), 0L);
553 X509_NAME * name = X509_NAME_new();
554 for (i = 0; i < info.GetSize(); i++)
555 X509_NAME_add_entry_by_NID(name,
556 info.GetKeyAt(i),
557 MBSTRING_ASC,
558 (unsigned char *)(const char *)info.GetDataAt(i),
559 -1,-1, 0);
560 X509_set_issuer_name(certificate, name);
561 X509_set_subject_name(certificate, name);
562 X509_NAME_free(name);
564 X509_gmtime_adj(X509_get_notBefore(certificate), 0);
565 X509_gmtime_adj(X509_get_notAfter(certificate), (long)60*60*24*365*5);
567 X509_PUBKEY * pubkey = X509_PUBKEY_new();
568 if (pubkey != NULL) {
569 X509_PUBKEY_set(&pubkey, privateKey);
570 EVP_PKEY * pkey = X509_PUBKEY_get(pubkey);
571 X509_set_pubkey(certificate, pkey);
572 EVP_PKEY_free(pkey);
573 X509_PUBKEY_free(pubkey);
575 if (X509_sign(certificate, privateKey, EVP_md5()) > 0)
576 return TRUE;
580 X509_free(certificate);
581 certificate = NULL;
582 return FALSE;
586 PBYTEArray PSSLCertificate::GetData() const
588 PBYTEArray data;
590 if (certificate != NULL) {
591 BYTE * certPtr = data.GetPointer(i2d_X509(certificate, NULL));
592 i2d_X509(certificate, &certPtr);
595 return data;
599 PString PSSLCertificate::AsString() const
601 return PBase64::Encode(GetData());
605 BOOL PSSLCertificate::Load(const PFilePath & certFile, PSSLFileTypes fileType)
607 if (certificate != NULL) {
608 X509_free(certificate);
609 certificate = NULL;
612 PSSL_BIO in;
613 if (!in.OpenRead(certFile)) {
614 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
615 return FALSE;
618 if (fileType == PSSLFileTypeDEFAULT)
619 fileType = certFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
621 switch (fileType) {
622 case PSSLFileTypeASN1 :
623 certificate = d2i_X509_bio(in, NULL);
624 if (certificate != NULL)
625 return TRUE;
627 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
628 break;
630 case PSSLFileTypePEM :
631 certificate = PEM_read_bio_X509(in, NULL, NULL, NULL);
632 if (certificate != NULL)
633 return TRUE;
635 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
636 break;
638 default :
639 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
642 return FALSE;
646 BOOL PSSLCertificate::Save(const PFilePath & certFile, BOOL append, PSSLFileTypes fileType)
648 if (certificate == NULL)
649 return FALSE;
651 PSSL_BIO out;
652 if (!(append ? out.OpenAppend(certFile) : out.OpenWrite(certFile))) {
653 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
654 return FALSE;
657 if (fileType == PSSLFileTypeDEFAULT)
658 fileType = certFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
660 switch (fileType) {
661 case PSSLFileTypeASN1 :
662 if (i2d_X509_bio(out, certificate))
663 return TRUE;
665 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
666 break;
668 case PSSLFileTypePEM :
669 if (PEM_write_bio_X509(out, certificate))
670 return TRUE;
672 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
673 break;
675 default :
676 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
679 return FALSE;
683 ///////////////////////////////////////////////////////////////////////////////
685 PSSLDiffieHellman::PSSLDiffieHellman()
687 dh = NULL;
691 PSSLDiffieHellman::PSSLDiffieHellman(const PFilePath & dhFile,
692 PSSLFileTypes fileType)
694 dh = NULL;
695 Load(dhFile, fileType);
699 PSSLDiffieHellman::PSSLDiffieHellman(const BYTE * pData, PINDEX pSize,
700 const BYTE * gData, PINDEX gSize)
702 dh = DH_new();
703 if (dh == NULL)
704 return;
706 dh->p = BN_bin2bn(pData, pSize, NULL);
707 dh->g = BN_bin2bn(gData, gSize, NULL);
708 if (dh->p != NULL && dh->g != NULL)
709 return;
711 DH_free(dh);
712 dh = NULL;
716 PSSLDiffieHellman::PSSLDiffieHellman(const PSSLDiffieHellman & diffie)
718 dh = diffie.dh;
722 PSSLDiffieHellman & PSSLDiffieHellman::operator=(const PSSLDiffieHellman & diffie)
724 if (dh != NULL)
725 DH_free(dh);
726 dh = diffie.dh;
727 return *this;
731 PSSLDiffieHellman::~PSSLDiffieHellman()
733 if (dh != NULL)
734 DH_free(dh);
737 #ifdef __BEOS__
738 // 2/21/04 Yuri Kiryanov - fix for compiler choke on BeOS for usage of
739 // SSL function d2i_DHparams_bio below in PSSLDiffieHellman::Load
740 #undef d2i_DHparams_bio
741 #define d2i_DHparams_bio(bp,x) \
742 (DH *)ASN1_d2i_bio( \
743 (char *(*)(...))(void *)DH_new, \
744 (char *(*)(...))(void *)d2i_DHparams, \
745 (bp), \
746 (unsigned char **)(x) \
748 #endif
750 BOOL PSSLDiffieHellman::Load(const PFilePath & dhFile,
751 PSSLFileTypes fileType)
753 if (dh != NULL) {
754 DH_free(dh);
755 dh = NULL;
758 PSSL_BIO in;
759 if (!in.OpenRead(dhFile)) {
760 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
761 return FALSE;
764 if (fileType == PSSLFileTypeDEFAULT)
765 fileType = dhFile.GetType() == ".pem" ? PSSLFileTypePEM : PSSLFileTypeASN1;
767 switch (fileType) {
768 case PSSLFileTypeASN1 :
769 dh = d2i_DHparams_bio(in, NULL);
770 if (dh != NULL)
771 return TRUE;
773 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_ASN1_LIB);
774 break;
776 case PSSLFileTypePEM :
777 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
778 if (dh != NULL)
779 return TRUE;
781 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
782 break;
784 default :
785 SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
788 return FALSE;
792 ///////////////////////////////////////////////////////////////////////////////
794 static void LockingCallback(int mode, int n, const char * /*file*/, int /*line*/)
796 static PSSLMutexArray mutexes;
797 if ((mode & CRYPTO_LOCK) != 0)
798 mutexes[n].Wait();
799 else
800 mutexes[n].Signal();
804 static int VerifyCallBack(int ok, X509_STORE_CTX * ctx)
806 X509 * err_cert = X509_STORE_CTX_get_current_cert(ctx);
807 //int err = X509_STORE_CTX_get_error(ctx);
809 // get the subject name, just for verification
810 char buf[256];
811 X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
813 PTRACE(1, "SSL\tVerify callback depth "
814 << X509_STORE_CTX_get_error_depth(ctx)
815 << " : cert name = " << buf);
817 return ok;
821 static void PSSLAssert(const char * msg)
823 char buf[256];
824 strcpy(buf, msg);
825 ERR_error_string(ERR_peek_error(), &buf[strlen(msg)]);
826 PTRACE(1, "SSL\t" << buf);
827 PAssertAlways(buf);
831 PSSLContext::PSSLContext(const void * sessionId, PINDEX idSize)
833 static PMutex InitialisationMutex;
834 InitialisationMutex.Wait();
836 static BOOL needInitialisation = TRUE;
837 if (needInitialisation) {
838 SSL_load_error_strings();
839 OpenSSL_add_ssl_algorithms();
841 // Seed the random number generator
842 BYTE seed[128];
843 for (size_t i = 0; i < sizeof(seed); i++)
844 seed[i] = (BYTE)rand();
845 RAND_seed(seed, sizeof(seed));
847 // set up multithread stuff
848 CRYPTO_set_locking_callback(LockingCallback);
850 needInitialisation = FALSE;
853 InitialisationMutex.Signal();
855 // create the new SSL context
856 SSL_METHOD * meth = SSLv23_method();
857 context = SSL_CTX_new(meth);
858 if (context == NULL)
859 PSSLAssert("Error creating context: ");
861 // Shutdown
862 SSL_CTX_set_quiet_shutdown(context, 1);
864 // Set default locations
865 if (!SSL_CTX_load_verify_locations(context, NULL, ".") ||
866 !SSL_CTX_set_default_verify_paths(context))
867 PSSLAssert("Cannot set CAfile and path: ");
869 if (sessionId != NULL) {
870 if (idSize == 0)
871 idSize = ::strlen((const char *)sessionId)+1;
872 SSL_CTX_set_session_id_context(context, (const BYTE *)sessionId, idSize);
873 SSL_CTX_sess_set_cache_size(context, 128);
876 // set default verify mode
877 SSL_CTX_set_verify(context, SSL_VERIFY_NONE, VerifyCallBack);
881 PSSLContext::~PSSLContext()
883 SSL_CTX_free(context);
887 BOOL PSSLContext::SetCAPath(const PDirectory & caPath)
889 PString path = caPath.Left(caPath.GetLength()-1);
890 if (!SSL_CTX_load_verify_locations(context, NULL, path))
891 return FALSE;
893 return SSL_CTX_set_default_verify_paths(context);
897 BOOL PSSLContext::SetCAFile(const PFilePath & caFile)
899 if (!SSL_CTX_load_verify_locations(context, caFile, NULL))
900 return FALSE;
902 return SSL_CTX_set_default_verify_paths(context);
906 BOOL PSSLContext::UseCertificate(const PSSLCertificate & certificate)
908 return SSL_CTX_use_certificate(context, certificate) > 0;
912 BOOL PSSLContext::UsePrivateKey(const PSSLPrivateKey & key)
914 if (SSL_CTX_use_PrivateKey(context, key) <= 0)
915 return FALSE;
917 return SSL_CTX_check_private_key(context);
921 BOOL PSSLContext::UseDiffieHellman(const PSSLDiffieHellman & dh)
923 return SSL_CTX_set_tmp_dh(context, (dh_st *)dh) > 0;
927 BOOL PSSLContext::SetCipherList(const PString & ciphers)
929 if (ciphers.IsEmpty())
930 return FALSE;
932 return SSL_CTX_set_cipher_list(context, (char *)(const char *)ciphers);
936 /////////////////////////////////////////////////////////////////////////
938 // SSLChannel
941 PSSLChannel::PSSLChannel(PSSLContext * ctx, BOOL autoDel)
943 if (ctx != NULL) {
944 context = ctx;
945 autoDeleteContext = autoDel;
947 else {
948 context = new PSSLContext;
949 autoDeleteContext = TRUE;
952 ssl = SSL_new(*context);
953 if (ssl == NULL)
954 PSSLAssert("Error creating channel: ");
958 PSSLChannel::PSSLChannel(PSSLContext & ctx)
960 context = &ctx;
961 autoDeleteContext = FALSE;
963 ssl = SSL_new(*context);
967 PSSLChannel::~PSSLChannel()
969 // free the SSL connection
970 if (ssl != NULL)
971 SSL_free(ssl);
973 if (autoDeleteContext)
974 delete context;
978 BOOL PSSLChannel::Read(void * buf, PINDEX len)
980 flush();
982 channelPointerMutex.StartRead();
984 lastReadCount = 0;
986 BOOL returnValue = FALSE;
987 if (readChannel == NULL)
988 SetErrorValues(NotOpen, EBADF, LastReadError);
989 else if (readTimeout == 0 && SSL_pending(ssl) == 0)
990 SetErrorValues(Timeout, ETIMEDOUT, LastReadError);
991 else {
992 readChannel->SetReadTimeout(readTimeout);
994 int readResult = SSL_read(ssl, (char *)buf, len);
995 lastReadCount = readResult;
996 returnValue = readResult > 0;
997 if (readResult < 0 && GetErrorCode(LastReadError) == NoError)
998 ConvertOSError(-1, LastReadError);
1001 channelPointerMutex.EndRead();
1003 return returnValue;
1006 BOOL PSSLChannel::Write(const void * buf, PINDEX len)
1008 flush();
1010 channelPointerMutex.StartRead();
1012 lastWriteCount = 0;
1014 BOOL returnValue;
1015 if (writeChannel == NULL) {
1016 SetErrorValues(NotOpen, EBADF, LastWriteError);
1017 returnValue = FALSE;
1019 else {
1020 writeChannel->SetWriteTimeout(writeTimeout);
1022 int writeResult = SSL_write(ssl, (const char *)buf, len);
1023 lastWriteCount = writeResult;
1024 returnValue = lastWriteCount >= len;
1025 if (writeResult < 0 && GetErrorCode(LastWriteError) == NoError)
1026 ConvertOSError(-1, LastWriteError);
1029 channelPointerMutex.EndRead();
1031 return returnValue;
1035 BOOL PSSLChannel::Close()
1037 BOOL ok = SSL_shutdown(ssl);
1038 return PIndirectChannel::Close() && ok;
1042 BOOL PSSLChannel::ConvertOSError(int error, ErrorGroup group)
1044 Errors lastError = NoError;
1045 DWORD osError = 0;
1046 if (SSL_get_error(ssl, error) != SSL_ERROR_NONE && (osError = ERR_peek_error()) != 0) {
1047 osError |= 0x80000000;
1048 lastError = Miscellaneous;
1051 return SetErrorValues(lastError, osError, group);
1055 PString PSSLChannel::GetErrorText(ErrorGroup group) const
1057 if ((lastErrorNumber[group]&0x80000000) == 0)
1058 return PIndirectChannel::GetErrorText(group);
1060 char buf[200];
1061 return ERR_error_string(lastErrorNumber[group]&0x7fffffff, buf);
1065 BOOL PSSLChannel::Accept()
1067 if (IsOpen())
1068 return ConvertOSError(SSL_accept(ssl));
1069 return FALSE;
1073 BOOL PSSLChannel::Accept(PChannel & channel)
1075 if (Open(channel))
1076 return ConvertOSError(SSL_accept(ssl));
1077 return FALSE;
1081 BOOL PSSLChannel::Accept(PChannel * channel, BOOL autoDelete)
1083 if (Open(channel, autoDelete))
1084 return ConvertOSError(SSL_accept(ssl));
1085 return FALSE;
1089 BOOL PSSLChannel::Connect()
1091 if (IsOpen())
1092 return ConvertOSError(SSL_connect(ssl));
1093 return FALSE;
1097 BOOL PSSLChannel::Connect(PChannel & channel)
1099 if (Open(channel))
1100 return ConvertOSError(SSL_connect(ssl));
1101 return FALSE;
1105 BOOL PSSLChannel::Connect(PChannel * channel, BOOL autoDelete)
1107 if (Open(channel, autoDelete))
1108 return ConvertOSError(SSL_connect(ssl));
1109 return FALSE;
1113 BOOL PSSLChannel::UseCertificate(const PSSLCertificate & certificate)
1115 return SSL_use_certificate(ssl, certificate);
1119 void PSSLChannel::SetVerifyMode(VerifyMode mode)
1121 int verify;
1123 switch (mode) {
1124 default :
1125 case VerifyNone:
1126 verify = SSL_VERIFY_NONE;
1127 break;
1129 case VerifyPeer:
1130 verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
1131 break;
1133 case VerifyPeerMandatory:
1134 verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1137 SSL_set_verify(ssl, verify, VerifyCallBack);
1141 BOOL PSSLChannel::RawSSLRead(void * buf, PINDEX & len)
1143 if (!PIndirectChannel::Read(buf, len))
1144 return FALSE;
1146 len = GetLastReadCount();
1147 return TRUE;
1151 //////////////////////////////////////////////////////////////////////////
1153 // Low level interface to SSLEay routines
1157 #define PSSLCHANNEL(bio) ((PSSLChannel *)(bio->ptr))
1159 extern "C" {
1161 #if (OPENSSL_VERSION_NUMBER < 0x00906000)
1163 typedef int (*ifptr)();
1164 typedef long (*lfptr)();
1166 #endif
1168 static int Psock_new(BIO * bio)
1170 bio->init = 0;
1171 bio->num = 0;
1172 bio->ptr = NULL; // this is really (PSSLChannel *)
1173 bio->flags = 0;
1175 return(1);
1179 static int Psock_free(BIO * bio)
1181 if (bio == NULL)
1182 return 0;
1184 if (bio->shutdown) {
1185 if (bio->init) {
1186 PSSLCHANNEL(bio)->Shutdown(PSocket::ShutdownReadAndWrite);
1187 PSSLCHANNEL(bio)->Close();
1189 bio->init = 0;
1190 bio->flags = 0;
1192 return 1;
1196 static long Psock_ctrl(BIO * bio, int cmd, long num, void * /*ptr*/)
1198 switch (cmd) {
1199 case BIO_CTRL_SET_CLOSE:
1200 bio->shutdown = (int)num;
1201 return 1;
1203 case BIO_CTRL_GET_CLOSE:
1204 return bio->shutdown;
1206 case BIO_CTRL_FLUSH:
1207 return 1;
1210 // Other BIO commands, return 0
1211 return 0;
1215 static int Psock_read(BIO * bio, char * out, int outl)
1217 if (out == NULL)
1218 return 0;
1220 BIO_clear_retry_flags(bio);
1222 // Skip over the polymorphic read, want to do real one
1223 PINDEX len = outl;
1224 if (PSSLCHANNEL(bio)->RawSSLRead(out, len))
1225 return len;
1227 switch (PSSLCHANNEL(bio)->GetErrorCode(PChannel::LastReadError)) {
1228 case PChannel::Interrupted :
1229 case PChannel::Timeout :
1230 BIO_set_retry_read(bio);
1231 return -1;
1233 default :
1234 break;
1237 return 0;
1241 static int Psock_write(BIO * bio, const char * in, int inl)
1243 if (in == NULL)
1244 return 0;
1246 BIO_clear_retry_flags(bio);
1248 // Skip over the polymorphic write, want to do real one
1249 if (PSSLCHANNEL(bio)->PIndirectChannel::Write(in, inl))
1250 return PSSLCHANNEL(bio)->GetLastWriteCount();
1252 switch (PSSLCHANNEL(bio)->GetErrorCode(PChannel::LastWriteError)) {
1253 case PChannel::Interrupted :
1254 case PChannel::Timeout :
1255 BIO_set_retry_write(bio);
1256 return -1;
1258 default :
1259 break;
1262 return 0;
1266 static int Psock_puts(BIO * bio, const char * str)
1268 int n,ret;
1270 n = strlen(str);
1271 ret = Psock_write(bio,str,n);
1273 return ret;
1279 static BIO_METHOD methods_Psock =
1281 BIO_TYPE_SOCKET,
1282 "PTLib-PSSLChannel",
1283 #if (OPENSSL_VERSION_NUMBER < 0x00906000)
1284 (ifptr)Psock_write,
1285 (ifptr)Psock_read,
1286 (ifptr)Psock_puts,
1287 NULL,
1288 (lfptr)Psock_ctrl,
1289 (ifptr)Psock_new,
1290 (ifptr)Psock_free
1291 #else
1292 Psock_write,
1293 Psock_read,
1294 Psock_puts,
1295 NULL,
1296 Psock_ctrl,
1297 Psock_new,
1298 Psock_free
1299 #endif
1303 BOOL PSSLChannel::OnOpen()
1305 BIO * bio = BIO_new(&methods_Psock);
1306 if (bio == NULL) {
1307 SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
1308 return FALSE;
1311 // "Open" then bio
1312 bio->ptr = this;
1313 bio->init = 1;
1315 SSL_set_bio(ssl, bio, bio);
1316 return TRUE;
1320 //////////////////////////////////////////////////////////////////////////
1322 // misc unused code
1325 #if 0
1327 extern "C" {
1329 static verify_depth = 0;
1330 static verify_error = VERIFY_OK;
1332 // should be X509 * but we can just have them as char *.
1333 int verify_callback(int ok, X509 * xs, X509 * xi, int depth, int error)
1335 char *s;
1337 s = (char *)X509_NAME_oneline(X509_get_subject_name(xs));
1338 if (s == NULL) {
1339 // ERR_print_errors(bio_err);
1340 return(0);
1342 PError << "depth= " << depth << " " << (char *)s << endl;
1343 free(s);
1344 if (error == VERIFY_ERR_UNABLE_TO_GET_ISSUER) {
1345 s=(char *)X509_NAME_oneline(X509_get_issuer_name(xs));
1346 if (s == NULL) {
1347 PError << "verify error" << endl;
1348 //ERR_print_errors(bio_err);
1349 return(0);
1351 PError << "issuer = " << s << endl;
1352 free(s);
1355 if (!ok) {
1356 PError << "verify error:num=" << error << " " <<
1357 X509_cert_verify_error_string(error) << endl;
1358 if (verify_depth <= depth) {
1359 ok=1;
1360 verify_error=VERIFY_OK;
1361 } else {
1362 ok=0;
1363 verify_error=error;
1366 PError << "verify return:" << ok << endl;
1367 return(ok);
1372 #endif
1374 #endif // P_SSL
1377 // End of file ////////////////////////////////////////////////////////////////