/*****************************************************************************/ /* Sesola.c THE GNU GENERAL PUBLIC LICENSE APPLIES DOUBLY TO ANYTHING TO DO WITH AUTHENTICATION AND AUTHORIZATION! This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License, or any later version. > This package is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. GOOD ADVICE FROM THE README OF THE APACHE MOD_SSL SOURCE -------------------------------------------------------- You should be very sensible when using cryptography software, because just running an SSL server _DOES NOT_ mean your system is then secure! This is for a number of reasons. The following questions illustrate some of the problems. o SSL itself may not be secure. People think it is, do you? o Does this code implement SSL correctly? o Have the authors of the various components put in back doors? o Does the code take appropriate measures to keep private keys private? To what extent is your cooperation in this process required? o Is your system physically secure? o Is your system appropriately secured from intrusion over the network? o Whom do you trust? Do you understand the trust relationship involved in SSL certificates? Do your system administrators? o Are your keys, and keys you trust, generated careful enough to avoid reverse engineering of the private keys? o How do you obtain certificates, keys, and the like, securely? o Can you trust your users to safeguard their private keys? o Can you trust your browser to safeguard its generated private key? If you can't answer these questions to your personal satisfaction, then you usually have a problem. Even if you can, you may still _NOT_ be secure. Don't blame the authors if it all goes horribly wrong. Use it at your own risk! GENERAL ------- Be aware that export/import and/or use of cryptography software, or even just providing cryptography hooks, is illegal in some parts of the world. When you re-distribute this package or even email patches/suggestions to the author or other people PLEASE PAY CLOSE ATTENTION TO ANY APPLICABLE EXPORT/IMPORT LAWS. The author of this package is not liable for any violations you make here. Named "Sesola" to avoid any confusion and/or conflict with OpenSSL routines. SSL I/O is implemented as a OpenSSL BIO_METHOD, named "Sesola_method". It provides NON-BLOCKING SSL input/output. All routines that are part of this functionality are named "Sesola_..." and are grouped towards the end of this module. To provide some of the facilities available in this module it was necessary in places to directly access some of the internals of structures used by OpenSSL. Let's hope they don't change names too often! OPENSSL ------- With the retirement of Eric Young from OpenSource software and the creation of the OpenSSL organisation this module will be based on this group's packages and in particular the VMS port supported in large part by Richard Levitte. levitte@openssl.org http://www.openssl.org/~levitte/ The initial development was done using SSLeay v0.8.1 Modified for SSLeay 0.9.0b A great leap forward with OpenSSL v0.9.3 (thanks Richard) Tested against OpenSSL v0.9.4 Tested and modified for OpenSSL v0.9.5 Tested against OpenSSL v0.9.6 Tested against OpenSSL v0.9.6a Tested against OpenSSL v0.9.6b Tested against OpenSSL v0.9.6c Tested against OpenSSL v0.9.6d Tested against OpenSSL v0.9.6e Tested against OpenSSL v0.9.6f Tested against CPQ AXPVMS SSL V1.0-A Tested and modified for OpenSSL v0.9.7-beta Tested and (minor) modification for OpenSSL v0.9.8 Continued testing and configuration against OpenSSL v1.0.n Tested against HP AXPVMS SSL1 V1.0-2C Some code changes and conditional compilation required for OpenSSL v1.1.0(-pre6) A general hard-copy reference (and there aren't many around) that has been interesting and informative is "SSL and TLS, Designing and Building Secure Systems", Eric Rescorla, 2001, Addison-Wesley (ISBN-201-61598-3). Though don't blame the book's author for the code author's shortcomings! 2016 Update: some fifteen years later there is a substantial number more tomes. LINKING/BUILDING AGAINST OLDER OPENSSL -------------------------------------- Minimum supported OpenSSL version is v1.0.0. This precludes HP SSL V1.4 (based on OpenSSL 0.9.8) and earlier. It does allow building against HP SSL1 (based on 1.0.2c) and presumably later. CLIENT CERTIFICATE AUTHORIZATION -------------------------------- Thanks to Ralf S.Engelschall (rse@engelschall.com) and Apache 'mod_ssl' package for hints on how to do some of this (all bits broken in the 'technology transfer' are mine :^) Client certificates may be used for authorization, against a realm name "X509". This means that a client (user at a browser for instance) is prompted to supply a X509 certificate as the authorization source. No username/password is required, and the usual such dialogs are not generated. Remote-User - Fingerprint ------------------------- By default WASD uses the FINGERPRINT OF THE CERTIFICATE (without the usual byte-delimiting colons) as the identifying username of the client. For authorization purposes this username may used like any other (i.e. in rule access restriction lists, added to group lists, etc.) The usual certificate fingerprint looks like 10:6C:83:42:89:0A:17:03:AA:A5:17:31:7B:14:5B:F7 Such a 'fingerprint' username comprises 32 hexadecimal digits (without the usual byte-delimiting colons) and looks like 106C8342890A1703AAA517317B145BF7 Such a fingerprint-username is UNIQUE (based on the MD5 algorithm) and a USEFUL REPRESENTATION OF THE CERTIFICATE AND USER of the client, even though lacking slightly in admin-friendly information. Some CGI variables (.e.g the AUTH_X509... and AUTH_USER) provided more accessable information. A CGI script can easily be designed to capture (and perhaps email) this information for use by the administrator in setting up authorization, etc. Remote-User - Subject DN Record ------------------------------- Alternatively, using the directives described immediately below, it is possible to specify that the server should derive the remote-user information from a particular record in the client certificate Subject Distinguished Name. The length of this identifying string is limited by AUTH_MAX_USERNAME_LENGTH specified in AUTH.H and has all white-space converted to underscores (white-space is not allowed for when processing authentication "usernames"). Any of the records identified in the 'SesolaCertDnRec' structure encoded below may be used, but the more obvious candidates for such a use are /O=, /OU=, /CN=, /S=, /Uid= and /EMAIL=. Where multiple records can exist (e.g. common name) a pattern to match can be used to select one from between them. For example, to select a common name for remote user that uniformly begins with "WASD": [ru:/CN=wasd*] To eliminate any record containing "obvious" non-user characters, use a regular expression to selectively eliminate those records. For example, the following configuration excludes records containing values with a slash or equal symbol (note the escaped reserved '[' and ']' characters): [ru:/CN=^^\[^/=\]*$] Note that when using this facility it is almost mandatory to put some further access control on the certificate to prevent an unexpected, perhaps even a user-generated and installed certificate's field contents from being used (even considering CA verification is applied by default). The following is an example of such an WASD_CONFIG_AUTH entry. [X509] /VMS/* r+w,param="[ru:/CN=][is:/O=WASD\ HTTPd\ CA\ Cert]" Remote-User - Subject Alternative Name -------------------------------------- The "Subject Alternative Name" (SAN) X509 V3 extension, a common extension for providing identifying data in a certificate, can also be used to derive the remote user string. In fact any extension data may be so used. The basic syntax for this field is the full extension name, and the short-hand equivalent, shown below: [X509] /VMS/* r+w,param="[ru:X509v3_subject_Alternative_Name]" /VMS/* r+w,param="[ru:X509v3_SAN]" The SAN extension (in common with many others) may contain multiple data elements, each with a leading name, a colon, and a (if multi line) carriage-control terminated value. WASD parses these into unqiue fields (and from there into unique CGI variables/DCL symbols) using keywords fixed in function SesolaCertKeyword() and site configurable logical name WASD_X509_EXTENSION_KEYWORDS value. To select one of these fields, for example the common (Microsoft) user principal name (UPN), append the required field name to the extension name as shown in the following example (includes "shorthand" equivalents, along with the underscore and equate variants). Note that the identifying names are case-insensitive. [X509] /VMS/* r+w,param="[ru:X509V3_Subject_Alternative_Name_UserPrincipalName]" /VMS/* r+w,param="[ru:X509V3_Subject_Alternative_Name=UserPrincipalName]" /VMS/* r+w,param="[ru:X509v3_SAN_UPN]" /VMS/* r+w,param="[ru:X509v3_SAN=UPN]" /VMS/* r+w,param="[ru:X509V3_Subject_Alternative_Name_rfc822Name]" /VMS/* r+w,param="[ru:X509V3_Subject_Alternative_Name=rfc822Name]" /VMS/* r+w,param="[ru:X509v3_SAN_822]" /VMS/* r+w,param="[ru:X509v3_SAN=822]" Object Identifiers (OIDs) may be used for either record and field name (if an unknown otherName) by prefixing with "OID_". For example, the SAN may be alternatively selected, and the (Microsoft) user principal name, as in the following examples. [X509] /VMS/* r+w,param="[ru:OID_2_5_29_17]" /VMS/* r+w,param="[ru:OID_2_5_29_17_UPN]" /VMS/* r+w,param="[ru:OID_2_5_29_17=UPN]" /VMS/* r+w,param="[ru:X509v3_SAN_OID_1_3_6_1_20_2_3]" /VMS/* r+w,param="[ru:X509v3_SAN_OID=1_3_6_1_20_2_3]" X509 Extensions --------------- X509 certificate extensions are in general visible from WATCH and accessible via CGI variables (when enabled using SET SSLCGI=apache_mod_ssl_extens and SSLCGI=apache_mod_ssl_client path mappings). The identifying names derived from X509 extensions are built of the alphanumerics in the element names. Non-alphanumerics (e.g. spaces) have underscores substituted. Multiple underscore are compressed into singles. Where elements have identical names the first multiple has TWO underscores and the digit two appended, the second mutiple, two underscores and three appended, etc. Some examples: WWW_SSL_CLIENT_E_X509V3_CERTIFICATE_POLICIES == "Policy: 1.3.6.1.4.1.13569.10.20.4.1.1. CPS: http://pki.sda.za/tst/pki." WWW_SSL_CLIENT_E_X509V3_CERTIFICATE_POLICIES_CPS == "http://pki.sda.za/tst/pki" WWW_SSL_CLIENT_E_X509V3_CERTIFICATE_POLICIES_POLICY == "1.3.6.1.4.1.13569.10.20.4.1.1" WWW_SSL_CLIENT_E_X509V3_CRL_DISTRIBUTION_POINTS == ".Full Name:. URI:http://pki.sda.za/tst/pki/crl/TSTGLOBAL_CA-Class_D-\ User1(1).crl. URI:ldap:///CN=CA%20-%20Class%20D%20-%20User1\ (1),CN=TM88,CN=PDC,CN=P%20Key%20Services,CN=Services,CN=Config\ uration,DC=tst,DC=loc?certificateRevocationList?base?objectClass=cRLDis\ tributionPoint." WWW_SSL_CLIENT_E_X509V3_CRL_DISTRIBUTION_POINTS_FULL_NAME == "" WWW_SSL_CLIENT_E_X509V3_CRL_DISTRIBUTION_POINTS_URI == "http://pki.sda.za/tst/pki/crl/TSTGLOBAL_CA-Class_D-User1(1).crl" WWW_SSL_CLIENT_E_X509V3_CRL_DISTRIBUTION_POINTS_URI__2 == "ldap:///CN=CA%20-%20Class%20D%20-%20User1(1),CN=TM88,\ CN=PDC,CN=P20Key%20Services,CN=Services,CN=Configuration,DC=tst,\ DC=loc?certificateRevocationList?base?objectClass=cRLDistributionPoint" Client Cert Authorization Directives/Conditionals ------------------------------------------------- Conditionals provide extra control on which certificates and their content can and can't be used during client X509 authentication. They contain strings that set authentication parameters, or that are matched to specific elements of certificate and it's negotiation, and only if matched are the corresponding rules applied. The conditionals may contain the '*' and '%' wildcards, and optionally be negated by an '!' at the start of the conditional string. Conditionals are passed to the X509 authenticator via the 'param=""' in an authorization rule, and are delimited by '[' and ']'. Directives that set processing parameters must be one-per-directive. Conditional matching containing multiple, space-separated conditions may be included within one '[...]'. This behaves as a logical OR (i.e. the condition is true if only one is matched). Multiple '[...]' conditionals may be included. These act as a logical AND (i.e. all must have at least one condition matched). The result of an entire conditional may be optionally negated by prefixing the '[' with a '!'. Spaces must *not* be included in match strings. Reserved characters (i.e. spaces, exclamation marks and ']') may be escaped using a preceding backslash. Wildcards (asterisk and percent) cannot be escaped. Once verified the following conditionals control whether that certificate is allowed to be used for authentication. When string matching note that delimit wildcards are often required. Matching is case-insensitive. Note that the 'IS' and 'SU' conditionals each have two variants. As always the '@' is substituted here for '*' due to the issues with C comments. [!]IS:/record=string cert issuer DN record (e.g. [is:/O=VeriSign\ Inc.]) [!]IS:string cert entire issuer DN (e.g. [is:@/O=VeriSign\ Inc./@]) [!]SU:/record=string cert subject DN record (e.g. [su:/CN=Mark\ Daniel]) [!]SU:string cert entire subject DN (e.g. [su:@/CN=Mark%Daniel@]) [!]KS:string minimum keysize (e.g. [ks:128]) [!]CI:string negotiation cipher (e.g. [ci:RC4-MD5]) Note that the "IS:" and "SU:" conditionals each have a "specific-record" and an "entire-field" mode. If the conditional string begins with a slash then it is considered to be a match against a specified record's content within the field. If it begins with a wildcard then it is matched against the entire field's content. The following directives control how the certificate is to be processed. DP:integer set the CA verification depth LT:integer (re)set the authenticated session lifetime in minutes RU:string remote-user from certificate subject record (e.g. "/CN=") TO:integer set the session timeout in minutes ("EXPIRE" to expire) VF:OPTIONAL authorize even if the issuing CA cannot be verified VF:REQUIRED the issuing CA must be verified (default) VF:NONE do not get peer certificate (cancels any existing) Example WASD_CONFIG_AUTH entries: ["Just an example!"=X509] /cgi-bin/show_cert_details r,\ param="[VF:OPTIONAL_NO_CA]" /cgi-bin/show_verisign_details r,\ param="[VF:OPTIONAL_NO_CA] [IS:@/O=VeriSign\ Inc./@]" /some/path/or/other r+w, \ param="[DP:1] [TO:10] [SU:@/Email=Mark.Daniel@wasd.vsm.com.au]" /VMS/* r+w,param="[RU:/CN=]" Remember the WATCH facility provides considerable detail during the processing of all authorization mapping. FORWARD SECRECY ---------------- Forward secrecy, sometimes known as perfect forward secrecy (PFS), is a property of key-agreement protocols ensuring that a session key derived from a set of long-term keys cannot be compromised if one of the long-term keys is compromised in the future. OpenSSL supports forward secrecy using Diffie-Hellman key exchange with elliptic curve cryptography and this relies on generating emphemeral keys based on unique, "strong" primes. These are expensive to generate and so this is done infrequently, often during software build or installation. In the case of WASD, to maximise flexibility, these "strong" primes are stored in external PEM-format files, by default located in the WASD_ROOT:[LOCAL] directory, or optionally located using the logical name WASD_DH_PARAM:. These files are only briefly accessed during server startup SSL initialisation and the content later used during network connection SSL negotiation to generate the required ephemeral keys. Each file contains one prime for a certain key size, 512, 1024, etc., generated using the OpenSSL dhparam utility. $ openssl dhparam -out dh_param_.pem $ openssl dhparam -out dh_param_512.pem 512 $ openssl dhparam -out dh_param_1024.pem 1024 $ openssl dhparam -out dh_param_2048.pem 2048 https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_dh.html NOTE: Ephemeral keys form part of PFS, the others being selection and ordering of server ciphers, and ensuring the server determines the cipher used (+OP_CIPHER_SERVER_PREFERENCE). Further Note: There are also emphemeral RSA keys too but ... "It is therefore strongly recommended to not use ephemeral RSA key exchange and use DHE (Ephemeral Diffie-Hellman) key exchange instead in order to achieve forward secrecy"! https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_rsa_callback.html SESSION TICKETS --------------- RFC 5077 extends TLS via use of session tickets, instead of using session IDs and associated server cache. It defines a way to resume a TLS session without requiring that session-specific state is stored at the TLS server. When using session tickets, the TLS server stores its session-specific state in a session ticket and sends the session ticket to the TLS client for storing. The client resumes a TLS session by sending the session ticket to the server, and the server resumes the TLS session according to the session-specific state in the ticket. The session ticket is encrypted and authenticated by the server, and the server verifies its validity before using its contents. So as not to compromise Perfect Forward Secrecy, among other considerations, the encryption keys for Session IDs need to be changed periodically, with a maximum of daily suggested (by RFC4507 and confirmed by RFC5077). WASD does this using the DLM and an internal /DO= "TICKET=USE" command, distributing a 48 byte key structure to all instances, per-node and cluster-wide as applicable. The 48 byte set of keys is (re)generated daily at midnight (local time) and then distributed to all instances. At that point all extant session tickets are invalidated and require reissue. The command-line $ HTTPD /DO=TICKET=KEY will perform the same task ad hoc. Session tickets have a maximum 24 hour lifetime (~86340 seconds). Cluster-wide instances are specially accomodated ensuring only one node's InstanceSupervisor() TICKET=USE command is actually applied. This relies on a 64 byte Lock Value Block (VMS V8.2 and later). Where the 64 bytes LVB is not supported, neither is cluster-wide session keys and tickets. SSL COMMAND LINE OPTIONS ------------------------ The /SSL= qualifier allows certain SSL runtime parameters to be set. The default is support of SSLv2/SSLv3, certificate file provided by WASD_SSL_CERT, selected ciphers, and a session cache of 128. /SSL=SSLv2 support only SSLv2 /SSL=noSSLv2 turn SSLv2 option off /SSL=SSLv3 support only SSLv3 /SSL=noSSLv3 turn SSLv3 option off /SSL=(SSLv2,SSLv3) support both SSLv2 and SSLv3 /SSL=(SSLv2v3) support both SSLv2 and SSLv3 /SSL=TLSvALL support all available TLS protocol versions /SSL=TLSv1 support only TLSv1 (etc.) /SSL=noTLSv1 turn TLSv1 option off /SSL=TLSv1.1 support only TLSv1.1 /SSL=noTLSv1.1 turn TLSv1.1 option off /SSL=TLSv1.2 support only TLSv1.2 /SSL=noTLSv1.2 turn TLSv1.2 option off /SSL=TLSv1.3 support only TLSv1.3 /SSL=noTLSv1.3 turn TLSv1.3 option off /SSL=(CACHE=integer) maximum number of records in session cache /SSL=(CAFILE=file) location of default client verify CA certificate file /SSL=(CERT=file) location of default OpenSSL-PEM certificate file /SSL=(CIPHER=list) semi-colon-separated list of supported ciphers /SSL=(ICACHE=SIZE=integer) maximum number of records in instance cache /SSL=(ICACHE=RECORD=integer) size of record in instance cache (bytes) /SSL=(KEY=file) location of default OpenSSL-PEM private key file (if separate from the certificate information) /SSL=noSNI disable Server Name Indication (SNI) /SSL=(+OP_ALL,-OP_ALL,+,-) options on and off /SSL=(OPTIONS=0xnn) hex SSL_OP_.. options (see [.INCLUDE.OPENSSL]SSL.H) /SSL=(OPTIONS=+0xnn) OR in hex SSL_OP_.. options /SSL=(OPTIONS=-0xnn) AND out hex SSL_OP_.. options /SSL=(TIMEOUT=integer) set session cache timeout /SSL=(VERIFY=integer) set client certificate CA chin verification depth Multiple parameters may be included by separating with commas. Example: /SSL=(CERT=HT_ROOT:[LOCAL]SITE.PEM,SSLV2,SSLV3,TIMEOUT=10) CGI VARIABLES ------------- CGI variables for scripting and SSI environments can be selectively generated for "https:" requests. See the mapping SETting "SSLCGI=" in MAPURL.C module. The Apache mod_SSL variables are based on the v2.7 documentation by Ralf S. Engelschall (rse@engelschall.com). The Purveyor variables are based on Purveyor documentation. The client certificate authentication AUTH_X509... are always generated when there is X509 authentication. Client Certificate Authorization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ o AUTH_X509_CIPHER ......... name of the cipher in use o AUTH_X509_FINGERPRINT .... (MD5) fingerprint of X509 cert o AUTH_X509_ISSUER ......... CA of client X509 cert o AUTH_X509_KEYSIZE ........ 40, 56, 128, etc. o AUTH_X509_SUBJECT ........ client details of X509 cert Apache mod_SSL -like SSL variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ o HTTPS .................... "true" indicating it's SSL o SSL_PROTOCOL ............. version (e.g. "SSLv2", "SSLv3") o SSL_SESSION_ID ........... hex-encoded SSL session ID o SSL_CIPHER ............... cipher name (e.g. "RC4-MD5") o SSL_CIPHER_EXPORT ........ "true" if export grade o SSL_CIPHER_USEKEYSIZE .... bits cipher used for session o SSL_CIPHER_ALGKEYSIZE .... bits cipher is capable of supporting o SSL_CLIENT_M_VERSION ..... server cert version o SSL_CLIENT_M_SERIAL ...... server cert serial number o SSL_CLIENT_S_DN .......... subject cert distinguished name o SSL_CLIENT_S_DN_x509 ..... subject cert DN components o SSL_CLIENT_I_DN .......... issuer cert distinguished name o SSL_CLIENT_I_DN_x509 ..... issuer cert DN components o SSL_CLIENT_V_START ....... cert validity start date o SSL_CLIENT_V_END ......... cert validity end date o SSL_CLIENT_A_SIG ......... server cert signature algorithm o SSL_CLIENT_A_KEY ......... server cert public key algorithm o SSL_CLIENT_CERT .......... (see note in SesolaCgiVariablesApacheModSsl()) o SSL_SERVER_M_VERSION ..... server cert version o SSL_SERVER_M_SERIAL ...... server cert serial number o SSL_SERVER_S_DN .......... subject cert distinguished name o SSL_SERVER_S_DN_x509 ..... subject cert DN components o SSL_SERVER_I_DN .......... issuer cert distinguished name o SSL_SERVER_I_DN_x509 ..... issuer cert DN components o SSL_SERVER_V_START ....... cert validity start date o SSL_SERVER_V_END ......... cert validity end date o SSL_SERVER_A_SIG ......... server cert signature algorithm o SSL_SERVER_A_KEY ......... server cert public key algorithm o SSL_SERVER_CERT .......... (see note in SesolaCgiVariablesApacheModSsl()) o SSL_TLS_SNI .............. supplied Server Name Indication string o SSL_VERSION_INTERFACE .... WASD server software ID o SSL_VERSION_LIBRARY ...... OpenSSL version "Purveyor"-like security/SSL variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ o SECURITY_STATUS .......... "NONE" or "SSL" o SSL_CIPHER ............... name of the cipher in use o SSL_CIPHER_KEYSIZE ....... 40, 56, 128, etc. o SSL_CLIENT_CA ............ client cert authority o SSL_CLIENT_DN ............ client cert distinguished name o SSL_SERVER_CA ............ server cert authority o SSL_SERVER_DN ............ server cert distinguished name o SSL_VERSION .............. SSL version (e.g. "SSLv2") Certificate Extension variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ o SSL_CLIENT_E_ ...... name=value for client certificate o SSL_SERVER_E_ ...... name=value for server certificate LOGICAL NAMES ------------- The following logical names can be used to provide runtime information to the SSL functionality. Configuration options provided by /SSL= and [service] or /SERVICE= parameters override anything provided via these names. WASD_SSL_CAFILE location of default client verify CA certificate file WASD_SSL_CERT location of default OpenSSL-PEM certificate file WASD_SSL_CIPHER comma-separated list of default supported ciphers WASD_SSL_KEY location of default OpenSSL-PEM private key file WASD_SSL_PARAMS the equivalent functionality of the /SSL= qualifier WASD_OPENSSL_MEM_TRACK see SesolaInit() WASD_VM_OPENSSL see VmOpenSslInit() VERSION HISTORY --------------- 08-AUG-2020 MGD keep connect cert (->VerifyPeer) distinct from client cert bugfix; SesolaInitService() enable ALPN for TLSv1.3 02-AUG-2020 MGD bugfix; SesolaSNICallback() needs to propagate newly set context client verify parameters to SSL-specific 19-JUL-2020 MGD static fallback cert replaced by dynamic SesolaMkCert() 18-FEB-2020 MGD more OpenSSL memory instrumentation 19-JAN-2020 MGD verified against VSI SSL111 product 10-OCT-2018 MGD verified against OpenSSL v1.0.2 && v1.1.0 && v1.1.1 TLSv1.3 operational (though clients currently sparse) 03-JUN-2018 MGD SesolaControlReloadCerts() undoes SesolaControlReloadService() because it didn't actually work :-/ 02-FEB-2018 MGD SesolaControlReloadService() (was SesolaControlReloadCerts()) and uses latest configuration (e.g. certificate name) allowing (SSL parameter) modification without restart 10-OCT-2017 MGD SesolaReport..() allow reporting using an HTTP service 02-AUG-2017 MGD bugfix; rationalise as OpenSSL_version[_num]() becomes confused catering for OpenSSL v1.0.2 && v1.1.0 && v1.1.1 23-JUL-2017 MGD bugfix; SesolaOptionsAsString() doh! 09-JUN-2017 MGD SesolaInitService() when SSL_CTX_set_tmp_dh_callback() is enabled (DH_PARAM_*..PEM files present) ensure flag SSL_OP_CIPHER_SERVER_PREFERENCE is implicitly set SesolaOptionsAsString() teases out options into string 09-MAY-2017 MGD bugfix; SesolaSessionTicketUseKey() sigh some more :-[ 28-APR-2017 MGD SesolaSessionTicketUseKey() rework multi-instance/cluster (sigh! yes again; the lack of a test cluster these days) 16-MAR-2017 MGD TLSv1.3, noTLSv1.3 (for OpenSSL 1.1.1 and later) SesolaControlReloadCerts() implements /DO=SSL=CERT=LOAD bugfix; SesolaControlReloadCA() do not proactively X509_STORE_free() (leaves a dangling pointer?) 14-JAN-2017 MGD [ServiceSSLcert] specification can contain wildcard(s) 30-DEC-2016 MGD bugfix; SesolaSNICallback() port elimination 06-SEP-2016 MGD sesola.h |#include "openssl/rand.h"| to fix OpenSSL v1.1.0 static link error against rand_bytes() and rand_seed() 25-AUG-2016 MGD minimum supported OpenSSL version is now v1.0.0 which precludes HP SSL V1.4 (at least) (and WASD for VAX must be close to its last SSL-gasp too!) OpenSSL v1.1.0 required code changes including #if (OPENSSL_VERSION_NUMBER < 0x10100000L) in Sesola..() modules, and introducing a version dependent build use SSL_cache_hit() instead of ->hit use SSL_CTX_get/set_cert_store() instead of ->cert_store use SSL_SESSION_get_id() instead of ->session_id.. 14-JUN-2016 MGD SesolaSessionTicket..() refresh and coordinate the TLS session ticket key cluster-wide using the DLM [SSLsessionCacheMax] default (of zero) now disables in favour of the more efficient Session Ticket SesolaReport() kludge for UTC flavoured timestamps 05-JUN-2016 MGD [SSLverifyPeerDataMax] configuration directive 22-APR-2016 MGD bugfix; SesolaInit() session cache max -1 disables cache 07-FEB-2016 MGD SSL_CTX_set_ecdh_auto() set elliptic curves selection SesolaTmpDHCallback() further refinement 05-OCT-2015 MGD SesolaReport() list certificate extensions SesolaTmpDHCallback() improve DH*.PEM flexibility 25-AUG-2015 MGD SesolaCertParseDn() strncmp() not strsame() SesolaCertParseDn() select on pattern match 04-AUG-2015 MGD ALPN negotiation support for HTTP/2 22-FEB-2015 MGD strict transport security (RFC6707) 29-JAN-2015 MGD SesolaInitOptions() expand options keywords to include most SSL_OP_.. flags using the OpenSSL flag #define as the keyword minus the "SSL_" (e.g. OP_CIPHER_SERVER_PREFERENCE) SesolaTmpDHCallback() for ephemeral keys enabling "perfect forward secrecy" increase SESOLA_SSL_ACCEPT_MAX, SESOLA_SSL_CONNECT_MAX and SESOLA_SSL_SHUTDOWN_MAX updated the internal fallback PEM cert/key 20-DEC-2014 MGD SesolaInitService() and SesolaInitClientService() if cipher list begins '+', '-' or '!' append it to default 30-NOV-2014 MGD include SHA256 fingerprint 19-NOV-2014 MGD bugfix; SSL_CTX_set_default_passwd_cb_userdata() 16-NOV-2014 MGD NOTE: TLSv1, TLSv1.1, TLSv1.2 now ENABLED by default SSLv2 and SSLv3 are now DISABLED by default (as recommended post-POODLE) WASD_CONFIG_GLOBAL [SSL..] directives /SSL=(TLSvALL,TLSv1.1,noTLSv1.1,TLSv1.2,noTLSv1.2) removed /SSL=(2|3|23) which must be altered to SSLv2, etc. updated the internal fallback PEM cert/key bugfix; SSLv23_method() appears to be a Swiss-army knife significant rework of SSL version configuration 13-APR-2014 MGD SesolaVersion() include header origin and link data 14-SEP-2013 MGD rework SSL options parameters (and reporting) /SSL= +OP_ALL, -OP_ALL, +OP_LEG_REN, -OP_LEG_REN, +OP_NO_SES_RES, -OP_NO_SES_RES, +OP_SING_DH, -OP_SING_DH establish a default cipher list based on best practise SSL_get_certificate() bug workaround in SesolaReport() TLS1 Server Name Indication (SNI) extension (RFC 4366) /SSL=NOSNI parameter can disable establish and use a separate list of SSL services to expedite SSL service activities such as SNI default set options SSL_OP_SINGLE_DH_USE and SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION if session cache size is zero then SSL_CTX_sess_set_cache_mode() SSL_SESS_CACHE_OFF SesolaReport() add "Fingerprint:" to client cert data bugfix; SesolaInitService() (or refinement) SSL_CTX_set_session_id_context() against each service bugfix; SesolaReport() session hh:mm:ss minute 02-DEC-2012 MGD bugfix; SesolaInit() translate WASD_SSL_CIPHER logical name report SSL_CTX_set_cipher_list() error 01-JUL-2012 MGD bugfix; SesolaCertParseDn() return NULL if record not found 02-SEP-2011 MGD SesolaInit() default is SSLv2 off and SSLv3/TLSv1 on 13-FEB-2008 MGD SesolaReport() rework "Current Session" 'Session:' 28-MAR-2006 MGD added SSL=ICACHE=SIZE= and SSL=ICACHE=RECORD= to allow configuration of instance SSL session cache move SesolaCacheInit() out after AuthConfigInit() so that the record size can be increased if X509 realm is used 08-JUL-2005 MGD OpenSSL v0.9.8 changed macro name EVP_F_EVP_DECRYPTFINAL 25-MAY-2005 MGD SesolaWatchBioCallback() make >= 0 complete 19-APR-2005 MGD SesolaInitService() no longer needs to clone 21-AUG-2004 MGD significant refinements to SSL processing 22-JUL-2004 MGD persistent connections over SSL 14-JAN-2003 MGD DN record /email and /emailAddress 15-OCT-2002 MGD use internal ceritifcate and configuration in demo mode 28-AUG-2002 MGD add SHA1 fingerprint (everybody else has it ;^) 11-AUG-2002 MGD refine SesolaReport() for obtaining service ciphers (OpenSSLv0.9.6f/0.9.7-beta break it), built and tested against CPQ AXPVMS SSL V1.0-A, internal PEM cert/key as fallback; mainly for VMS (Open)SSL 03-JUL-2002 MGD refine SesolaReport() so it obtains the service certificate indirectly removing the need for SSL_LOCL.H (OpenSSL 0.9.7) 17-APR-2002 MGD bugfix; SesolaCertVerifyCallback() 02-JAN-2002 MGD rework SSL network functions into SESOLANET.C, add HTTP-SSL proxy (gateway) service initialization 27-OCT-2001 MGD break up a burgeoning SSL module into more source files 29-SEP-2001 MGD instance support 04-AUG-2001 MGD support module WATCHing 01-JUL-2001 MGD refine private key password request functionality 20-MAY-2001 MGD add [RU:/xx=] to allow a site to specify a certificate subject DN record as the authenticated remote-user, change [IS:/name=string] and [SU:/name=string] to allow individual issuer or subject DN records to be matched, private key password optionally can now be supplied via /DO=SSL=KEY=PASSWORD (see SesolaPrivateKeyPasswd()) 11-APR-2001 MGD remove http: check from SesolaAccept(), bugfix; SesolaFree() BioPtr 02-APR-2001 MGD refine SesolaClientCertVerifyCallback() 10-DEC-2000 MGD client certificate and authorization support, Richard Levitte in a recent email to vms-web-daemon@KJSL.COM points out using SSL_CTX_use_certificate_chain_file() is more versatile than SSL_CTX_use_certificate_file(), bugfix; SesolaCgiVariablesApacheModSsl() 22-NOV-2000 MGD where a service-specific certificate is supplied and no service-specific key assume the key is in the specific cert 17-OCT-2000 MGD modify SSL initialization so that "fallback" conditions (same port on same IP address) are more easily identified 26-SEP-2000 MGD built and verified against OpenSSL 0.9.6 change 'boolean' to 'BOOL' to accomodate new OpenSSL typedef 26-AUG-2000 MGD SesolaWatchPeek() 17-JUN-2000 MGD modifications for SERVICE.C requirements 06-MAY-2000 MGD added Apache mod_SSL style CGI variables 05-MAR-2000 MGD tested against OpenSSL v0.9.5 (SSL_OP_NON_EXPORT_FIRST generates error message, removed), use FaolToNet(), et.al. 23-DEC-1999 MGD modify NEEDSTRUCT reported by DECC v6.2 19-OCT-1999 MGD SesolaInitServiceList() service errors not fatal at startup 11-SEP-1999 MGD tested against OpenSSL v0.9.4, SSLeay no longer supported 18-AUG-1999 MGD add certificate CA and DN to SSL report, bugfix; certificate/key pairs when initializing service 12-JUN-1999 MGD refine SSL connection handling 26-MAY-1999 MGD allow some SSL options to be set if desired, set SSL_OP_NON_EXPORT_FIRST (for some certificate processing) 18-APR-1999 MGD improve error reporting during certificate loading 03-APR-1999 MGD support OpenSSL 0.9.3 (with initial, backward support for SSLeay 0.8 & 0.9), add WATCH callbacks 31-MAR-1999 MGD bugfix; report request from non-SSL port 20-JAN-1999 MGD report format refinement 07-NOV-1998 MGD WATCH facility 24-OCT-1998 MGD per-service context (implies per-service certificate) 01-JUN-1998 MGD builds OK with SSLeay 0.9.0b 25-JAN-1998 MGD initial development for v5.0, SSLeay v0.8.1 */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include /* application header files */ #define SESOLA_REQUIRED #include "Sesola.h" /* OpenSSL v0.9.8 changed macro name (provide some backward compatibility) */ #ifndef EVP_F_EVP_DECRYPTFINAL_EX # define EVP_F_EVP_DECRYPTFINAL_EX EVP_F_EVP_DECRYPTFINAL #endif /* not present with some earlier versions of HP SSL */ #ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION # define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L #endif /* don't exist with (at least) HP AXPVMS SSL V1.4 */ #ifndef SSL_OP_NO_TLSv1_1 #define SSL_OP_NO_TLSv1_1 0x10000000L #endif #ifndef SSL_OP_NO_TLSv1_2 #define SSL_OP_NO_TLSv1_2 0x08000000L #endif #ifndef SSL_OP_NO_TLSv1_3 #define SSL_OP_NO_TLSv1_3 0x20000000L #endif #define WASD_MODULE "SESOLA" /***************************************/ #ifdef SESOLA /* secure sockets layer */ /***************************************/ /******************/ /* global storage */ /******************/ BOOL ProtocolHttpsAvailable = true, ProtocolHttpsConfigured = false, ProtocolHttpsDisabled = false, SesolaMemTrack, SesolaMemVmTrack, SesolaPrivateKeyPasswdRequested, SesolaTLS1112, SesolaTLS13, SesolaSNI = true, SesolaVerifyCAConfigured; int SesolaDefaultVerifyDepth = SESOLA_DEFAULT_VERIFY_DEPTH, SesolaGblSecStructSize = sizeof(struct SesolaGblSecStruct), SesolaSessionCacheSize, SesolaSessionCacheTimeout = SESOLA_DEFAULT_CACHE_TIMEOUT, SesolaSessionLifetime, SesolaPrivateKeyPasswdAttempt, SesolaSessionTicketHit, SesolaSessionTicketMiss, SesolaSessionTicketNew, SesolaSSLoptions, SesolaVersionBitmap, SesolaVersionOptions, SesolaVerifyPeerDataMax; ulong SesolaOpenSslVersionNumber, SesolaFreeCount, SesolaMallocCount, SesolaPrevFreeCount, SesolaPrevMallocCount, SesolaPrevReallocCount, SesolaReallocCount; static ulong LastLookTime64; #ifndef __VAX __int64 SesolaFreeBytes, SesolaInUseBytes, SesolaMallocBytes, SesolaMaxBytes, SesolaPrevAvailBytes, SesolaPrevFreeBytes, SesolaPrevInUseBytes, SesolaPrevMallocBytes, SesolaPrevMaxBytes; #endif char *SesolaDefaultCertPtr, *SesolaDefaultCipherListPtr, *SesolaDefaultKeyPtr, *SesolaDefaultCaFilePtr, *SesolaDefaultStrictTransSecPtr, *SesolaTmpDHbitsPtr; char SesolaParams [256], SesolaVersionString [64]; /* the ticket key (being randomised) is also used as the session ID */ uchar /* size of tick_key_name + tick_hmac_key + tick_aes_key */ SesolaTicketKey [48]; ulong SesolaTicketKeyTime64 [2]; int SesolaTicketKeySuperDay; /* the session ID is generated using shared session ticket key data */ uchar SesolaSessionId [16]; /* < SSL_MAX_SSL_SESSION_ID_LENGTH (32) */ char ErrorSslPort [] = "This is an SSL ("https:") port.", HttpdSesola [] = " SSL", SesolaDefaultOptions [] = SESOLA_DEFAULT_OPTIONS, SesolaDefaultProtocol [] = SESOLA_DEFAULT_PROTOCOL; /* local symbols of OpenSSL functions - see LINKING WITH OLDER SSL above */ #if SESOLA_BEFORE_110 extern const SSL_METHOD *SSLv2_method(void); extern const SSL_METHOD *SSLv3_method(void); extern const SSL_METHOD *TLSv1_1_method(void); extern const SSL_METHOD *TLSv1_2_method(void); int (*Sesola_SSL_get_servername)(SSL*, int) = SSL_get_servername; int (*Sesola_TLSv1_1_method)() = TLSv1_1_method; int (*Sesola_TLSv1_2_method)() = TLSv1_2_method; int (*Sesola_SSLv2_method)() = SSLv2_method; int (*Sesola_SSLv3_method)() = SSLv3_method; #endif /* SESOLA_BEFORE_110 */ BIO *SesolaBioMemPtr; /********************/ /* external storage */ /********************/ extern BOOL CliDemo, Http2Enabled, HttpdServerStartup, ServiceWwwImplied; extern int EfnWait, ExitStatus, HttpdTickSecond, InstanceNodeConfig, InstanceNodeCurrent, OpcomMessages, SesolaCacheRecordMax, SesolaCacheRecordSize; extern int ToLowerCase[], ToUpperCase[]; extern ushort HttpdNumTime[]; extern ulong HttpdTime64[], HttpdStartTime64[], SysPrvMask[]; extern char ErrorSanityCheck[], ServerHostPort[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern HTTPD_PROCESS HttpdProcess; extern MSG_STRUCT Msgs; extern LIST_HEAD ServiceList; extern WATCH_STRUCT Watch; /* the link deposits an integer in this */ globalvalue int SESOLA_LIB_SSL_32; /*****************************************************************************/ /* */ SesolaInit () { char ch; char *cptr, *sptr, *zptr, *SslParamsPtr; char buf [256]; CONFIG_STRUCT *cfptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInit()"); #ifndef __VAX if (SysTrnLnm ("WASD_OPENSSL_MEM_TRACK")) { SesolaMemTrack = true; SesolaMemVmTrack = VmOpenSslInit (); if (!CRYPTO_set_mem_functions (SesolaMalloc, SesolaRealloc, SesolaFree)) ErrorExitVmsStatus (0, "CRYPTO_set_mem_functions()", FI_LI); } else #endif if (VmOpenSslInit ()) /* "WASD_VM_OPENSSL" */ { /* OpenSSL memory management using WASD's own VM.C module */ if (!CRYPTO_set_mem_functions (VmOpenSslMalloc, VmOpenSslRealloc, VmOpenSslFree)) ErrorExitVmsStatus (0, "CRYPTO_set_mem_functions()", FI_LI); } SesolaOpenSslVersionNumber = OpenSSL_version_num(); FaoToStdout ("%HTTPD-I-SSL, !AZ (0x!8XL)\n", OpenSSL_version (OPENSSL_VERSION), SesolaOpenSslVersionNumber); if (SesolaOpenSslVersionNumber < 0x10000000L) { FaoToStdout ("%HTTPD-W-SSL, unsupported version (minimum 1.0.0)\n"); ProtocolHttpsDisabled = true; return; } #if SESOLA_SINCE_110 SesolaTLS1112 = true; if (SesolaOpenSslVersionNumber >= 0x10101000) SesolaTLS13 = true; #else /* older versions of HP SSL product do not support SNI */ if (!(SesolaSNI = Sesola_SSL_get_servername != 0)) FaoToStdout ("%HTTPD-W-SSL, SSL_get_servername() unresolved\n"); /* HP SSL V1.4 (at least) do not support TLSv1.1 or TLSv1.2 */ if (Sesola_TLSv1_1_method == 0) FaoToStdout ("%HTTPD-W-SSL, TLSv1_1_method() unresolved\n"); if (Sesola_TLSv1_2_method == 0) FaoToStdout ("%HTTPD-W-SSL, TLSv1_2_method() unresolved\n"); SesolaTLS1112 = (Sesola_TLSv1_1_method != 0 && Sesola_TLSv1_2_method != 0); /* linking with default build of OpenSSL v1.1.0-pre5 */ if (Sesola_SSLv2_method == 0) FaoToStdout ("%HTTPD-W-SSL, SSLv2_method() unresolved\n"); if (Sesola_SSLv3_method == 0) FaoToStdout ("%HTTPD-W-SSL, SSLv3_method() unresolved\n"); #endif /* SESOLA_SINCE_110 */ /* command-line parameters, if none then use WASD_SSL_PARAMS */ SslParamsPtr = SesolaParams; if (!*SslParamsPtr) if (!(SslParamsPtr = v10orPrev10(CONFIG_SSL_PARAMS,0))) SslParamsPtr = ""; if (strsame (SslParamsPtr, "/NOSSL", -1)) { ProtocolHttpsDisabled = true; FaoToStdout ("%HTTPD-W-SSL, disabled via /NOSSL\n"); return; } cfptr = &Config; if (cfptr->cfSesola.Disabled) { ProtocolHttpsDisabled = true; FaoToStdout ("%HTTPD-W-SSL, disabled via [SecureSocket]\n"); return; } /* lots and lots of non-determinate stuff */ RAND_seed (HttpdGblSecPtr, sizeof(HTTPD_GBLSEC)); #if SESOLA_SINCE_110 OPENSSL_init_ssl (0, NULL); #else SSL_load_error_strings (); SSL_library_init (); #endif /* see [.APPS]S_SERVER.C */ SesolaSSLoptions = SSL_OP_ALL; SesolaSSLoptions |= SSL_OP_SINGLE_DH_USE; SesolaSSLoptions |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; /*********************************/ /* WASD_CONFIG_GLOBAL directives */ /*********************************/ if (cfptr->cfSesola.ProtocolVersion[0]) SesolaInitContext (cfptr->cfSesola.ProtocolVersion, NULL); if (cfptr->cfSesola.CipherList[0]) SesolaDefaultCipherListPtr = cfptr->cfSesola.CipherList; if (cfptr->cfSesola.ServerCert[0]) SesolaDefaultCertPtr = cfptr->cfSesola.ServerCert; if (cfptr->cfSesola.PrivateKey[0]) SesolaDefaultKeyPtr = cfptr->cfSesola.PrivateKey; if (cfptr->cfSesola.ProtocolOptions[0]) SesolaInitOptions (cfptr->cfSesola.ProtocolOptions, &SesolaSSLoptions); else SesolaInitOptions (SesolaDefaultOptions, &SesolaSSLoptions); if (cfptr->cfSesola.StrictTransSec[0]) SesolaDefaultStrictTransSecPtr = cfptr->cfSesola.StrictTransSec; if (cfptr->cfSesola.SessionCacheMax < 0) SesolaSessionCacheSize = 0; else if (cfptr->cfSesola.SessionCacheMax) SesolaSessionCacheSize = cfptr->cfSesola.SessionCacheMax; if (cfptr->cfSesola.VerifyPeerDataMax < 0) SesolaVerifyPeerDataMax = 0; else if (cfptr->cfSesola.VerifyPeerDataMax) SesolaVerifyPeerDataMax = cfptr->cfSesola.VerifyPeerDataMax; else SesolaVerifyPeerDataMax = SESOLA_VERIFY_PEER_DATA_MAX_DEFAULT; SesolaVerifyPeerDataMax *= 1024; /* in kBytes */ if (cfptr->cfSesola.InstanceSessionCacheMax) SesolaCacheRecordMax = cfptr->cfSesola.InstanceSessionCacheMax; if (cfptr->cfSesola.InstanceSessionCacheSize) SesolaCacheRecordSize = cfptr->cfSesola.InstanceSessionCacheSize; if (cfptr->cfSesola.VerifyPeer) if (!(SesolaDefaultVerifyDepth = cfptr->cfSesola.VerifyPeerDepth)) SesolaDefaultVerifyDepth = 1; if (cfptr->cfSesola.VerifyPeerCAFile[0]) SesolaDefaultCaFilePtr = cfptr->cfSesola.VerifyPeerCAFile; SesolaSessionLifetime = cfptr->cfSesola.SessionLifetime; /*********************************************/ /* command-line overrides WASD_CONFIG_GLOBAL */ /*********************************************/ /* lots of ill-defined historical hodge-podge catered for here */ cptr = SslParamsPtr; while (*cptr) { while (*cptr == '(' || *cptr == ',' || *cptr == ')') cptr++; sptr = cptr; while (*cptr && *cptr != ',' && *cptr != ')') cptr++; if (ch = *(zptr = cptr)) *cptr++ = '\0'; if (!*sptr) break; if (strsame (sptr, "CAFILE=", 7)) SesolaDefaultCaFilePtr = sptr + 7; else if (strsame (sptr, "CERT=", 5)) SesolaDefaultCertPtr = sptr + 5; else if (strsame (sptr, "CIPHER=", 7)) SesolaDefaultCipherListPtr = sptr + 7; else if (strsame (sptr, "CACHE=", 6)) SesolaSessionCacheSize = atoi(sptr+6); else if (strsame (sptr, "ICACHE=SIZE=", 12)) SesolaCacheRecordMax = atoi(sptr+12); else if (strsame (sptr, "ICACHE=RECORD=", 14)) SesolaCacheRecordSize = atoi(sptr+14); else if (strsame (sptr, "KEY=", 4)) SesolaDefaultKeyPtr = sptr + 4; else if (strsame (sptr, "OPTIONS=", 8)) { /* special case kludge - allow for parentheses :-( */ sptr += 8; if (*sptr == '(') { *zptr = ch; cptr = ++sptr; while (*cptr && *cptr != ')') cptr++; if (ch = *(zptr = cptr)) *cptr++ = '\0'; } SesolaInitOptions (sptr, &SesolaSSLoptions); } else if (strsame (sptr, "+OP_", 4) || strsame (sptr, "+0x", 3)) SesolaInitOptions (sptr, &SesolaSSLoptions); else if (strsame (sptr, "-OP_", 4) || strsame (sptr, "-0x", 3)) SesolaInitOptions (sptr, &SesolaSSLoptions); else if (strsame (sptr, "SNI", -1)) SesolaSNI = true; else if (strsame (sptr, "NOSNI", -1)) SesolaSNI = false; else if (strsame (sptr, "STRICT=", 7)) SesolaDefaultStrictTransSecPtr = sptr + 7; else if (strsame (sptr, "TIMEOUT=", 8)) SesolaSessionCacheTimeout = atoi(sptr+8); else if (strsame (sptr, "VERIFY=", 7)) SesolaDefaultVerifyDepth = atoi(sptr+7); else /* if not an SSL version parameter */ if (!(strsame(sptr,"SSLv",4) || strsame(sptr,"noSSLv",6) || strsame(sptr,"TLSv",4) || strsame(sptr,"noTLSv",6))) { FaoToStdout ("%HTTPD-E-SSL, unknown SSL parameter\n \\!AZ\\\n", sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } *zptr = ch; } /* process the /SSL= protocol version keywords */ SesolaInitContext (SslParamsPtr, NULL); /************/ /* finalise */ /************/ /* if no protocol preferences expressed in WASD_CONFIG_GLOBAL or /SSL= */ if (!SesolaVersionBitmap) SesolaInitContext (SesolaDefaultProtocol, NULL); FaoToStdout ("-SSL-I-PROTOCOL, !AZ\n", SesolaVersionString); FaoToStdout ("-SSL-I-OPTIONS, 0x!8XL\n", SesolaSSLoptions); FaoToStdout ("-SSL-!AZ-SNI, Server Name Indication !AZ\n", SesolaSNI ? "I" : "W", SesolaSNI ? "enabled" : "disabled"); /* initialise emphemeral DH params */ SesolaTmpDHCallback (NULL, 0, 0); /* initialise the session ticket key */ SesolaSessionTicketNewKey (); if (!SesolaDefaultCertPtr) SesolaDefaultCertPtr = v10orPrev10(CONFIG_SSL_CERT,0); if (!SesolaDefaultKeyPtr) SesolaDefaultKeyPtr = v10orPrev10(CONFIG_SSL_KEY,0); if (!SesolaDefaultCaFilePtr) SesolaDefaultCaFilePtr = v10orPrev10(CONFIG_SSL_CAFILE,0); if (!SesolaDefaultCipherListPtr) if (SesolaDefaultCipherListPtr = v10orPrev10(CONFIG_SSL_CIPHER,0)) if (cptr = SysTrnLnm (SesolaDefaultCipherListPtr)) { SesolaDefaultCipherListPtr = VmGet (strlen(cptr)+1); strcpy (SesolaDefaultCipherListPtr, cptr); } /* * A single, global memory BIO can be used when puts(), gets() and other * OpenSSL string operations are required provided; 1) they are properly * BIO_reset() after use (and before if you're belt and braces), and 2) * only used within a (single) AST context (i.e. no concurrent usage). */ SesolaBioMemPtr = BIO_new (BIO_s_mem()); if (!SesolaBioMemPtr) { FaoToStdout ("-SSL-E-BIO, BIO_new(BIO_s_mem()) failed\n"); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } /*****************************************************************************/ /* Establish an SSL context using a (WASD) protocol configuration string (from either the command-line or service protocol configuration field. It ignores (non-protocol) keywords it doesn't recognise. If a service structure is supplied it sets that service's SSL context and protocol version string. If no service struct is passed it configures global values. Returns the number of protocols configured. */ void SesolaInitContext ( char* ConfigString, SESOLA_CONTEXT *scptr ) { int SSLversion, SSLoptions, TLSmax, TLSmin, VersionCount; uint bit; char ch; char *cptr, *sptr, *zptr; SSL_CTX *SslCtx; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInitContext() !&Z", ConfigString); if (ProtocolHttpsDisabled) return; SslCtx = NULL; SSLoptions = SSLversion = 0; cptr = ConfigString; while (*cptr) { /* kludge: will see but NO need to allow for (..) */ while (*cptr == '(' || *cptr == ',' || *cptr == ')') cptr++; sptr = cptr; while (*cptr && *cptr != ',' && *cptr != ')') cptr++; if (ch = *(zptr = cptr)) *cptr++ = '\0'; if (!*sptr) break; #if SESOLA_SINCE_110 if (strsame (sptr, "SSLv2", -1) || strsame (sptr, "+SSLv2", -1) || strsame (sptr, "noSSLv2", -1) || strsame (sptr, "-SSLv2", -1) || strsame (sptr, "SSLv3", -1) || strsame (sptr, "+SSLv3", -1) || strsame (sptr, "noSSLv3", -1) || strsame (sptr, "-SSLv3", -1) || strsame (sptr, "SSLv2v3", -1) || strsame (sptr, "+SSLv2v3", -1)) FaoToStdout ("-SSL-W-PROTOCOL, keyword obsolete\n \\!AZ\\\n", sptr); else #else /* SESOLA_SINCE_110 */ if (strsame (sptr, "SSLv2", -1) || strsame (sptr, "+SSLv2", -1)) SSLversion |= SESOLA_SSLV2; else if (strsame (sptr, "noSSLv2", -1) || strsame (sptr, "-SSLv2", -1)) SSLversion &= ~SESOLA_SSLV2; else if (strsame (sptr, "SSLv3", -1) || strsame (sptr, "+SSLv3", -1)) SSLversion |= SESOLA_SSLV3; else if (strsame (sptr, "noSSLv3", -1) || strsame (sptr, "-SSLv3", -1)) SSLversion &= ~SESOLA_SSLV3; else if (strsame (sptr, "SSLv2v3", -1) || strsame (sptr, "+SSLv2v3", -1)) SSLversion |= SESOLA_SSLV2 | SESOLA_SSLV3; else #endif /* SESOLA_SINCE_110 */ if (strsame (sptr, "TLSvALL", -1)) { /* all (known) TLS protocol versions */ SSLversion |= SESOLA_TLSV1; if (SesolaTLS13) { SSLversion |= SESOLA_TLSV1_1; SSLversion |= SESOLA_TLSV1_2; SSLversion |= SESOLA_TLSV1_3; } else if (SesolaTLS1112) { SSLversion |= SESOLA_TLSV1_1; SSLversion |= SESOLA_TLSV1_2; } } else if (strsame (sptr, "TLSv1", -1) || strsame (sptr, "+TLSv1", -1)) SSLversion |= SESOLA_TLSV1; else if (strsame (sptr, "noTLSv1", -1) || strsame (sptr, "-TLSv1", -1)) SSLversion &= ~SESOLA_TLSV1; else /* TLS 1.1 and TLS 1.2 */ if (SesolaTLS1112 && strsame (sptr, "TLSv1.1", -1) || strsame (sptr, "+TLSv1.1", -1) || strsame (sptr, "TLSv1_1", -1)) SSLversion |= SESOLA_TLSV1_1; else if (SesolaTLS1112 && (strsame (sptr, "noTLSv1.1", -1) || strsame (sptr, "-TLSv1.1", -1) || strsame (sptr, "noTLSv1_1", -1) || strsame (sptr, "-TLSv1_1", -1))) SSLversion &= ~SESOLA_TLSV1_1; else if (SesolaTLS1112 && strsame (sptr, "TLSv1.2", -1) || strsame (sptr, "+TLSv1.2", -1) || strsame (sptr, "TLSv1_2", -1)) SSLversion |= SESOLA_TLSV1_2; else if (SesolaTLS1112 && (strsame (sptr, "noTLSv1.2", -1) || strsame (sptr, "-TLSv1.2", -1) || strsame (sptr, "noTLSv1_2", -1) || strsame (sptr, "-TLSv1_2", -1))) SSLversion &= ~SESOLA_TLSV1_2; else #if SESOLA_SINCE_111 /* TLS 1.3 */ if (SesolaTLS13 && strsame (sptr, "TLSv1.3", -1) || strsame (sptr, "+TLSv1.3", -1) || strsame (sptr, "TLSv1_3", -1)) SSLversion |= SESOLA_TLSV1_3; else if (SesolaTLS13 && (strsame (sptr, "noTLSv1.3", -1) || strsame (sptr, "-TLSv1.3", -1) || strsame (sptr, "noTLSv1_3", -1) || strsame (sptr, "-TLSv1_3", -1))) SSLversion &= ~SESOLA_TLSV1_3; else #endif /* SESOLA_SINCE_111 */ if (strsame(sptr,"SSLv",4) || strsame(sptr,"+SSLv",5) || strsame(sptr,"TLSv",4) || strsame(sptr,"+TLSv",5) || strsame(sptr,"noSSLv",6) || strsame(sptr,"-SSLv",5) || strsame(sptr,"noTLSv",6) || strsame(sptr,"-TLSv",5)) FaoToStdout ("-SSL-W-PROTOCOL, unknown keyword\n \\!AZ\\\n", sptr); *zptr = ch; } VersionCount = 0; for (bit = 1; bit; bit = bit << 1) if (SSLversion & bit) VersionCount++; if (!VersionCount && scptr) { /* when configuring a service use the server default */ SSLversion = SesolaVersionBitmap; for (bit = 1; bit; bit = bit << 1) if (SSLversion & bit) VersionCount++; } if (!VersionCount) return; if (VersionCount > 1) { #if SESOLA_BEFORE_110 /* swiss-army knife method then disable using SesolaVersionOptions */ if (!(SSLversion & SESOLA_SSLV2)) SSLoptions |= SSL_OP_NO_SSLv2; if (!(SSLversion & SESOLA_SSLV3)) SSLoptions |= SSL_OP_NO_SSLv3; #endif if (!(SSLversion & SESOLA_TLSV1)) SSLoptions |= SSL_OP_NO_TLSv1; if (!(SSLversion & SESOLA_TLSV1_1)) SSLoptions |= SSL_OP_NO_TLSv1_1; if (!(SSLversion & SESOLA_TLSV1_2)) SSLoptions |= SSL_OP_NO_TLSv1_2; if (!(SSLversion & SESOLA_TLSV1_3)) SSLoptions |= SSL_OP_NO_TLSv1_3; } #if SESOLA_BEFORE_110 if (VersionCount > 1) SslCtx = SSL_CTX_new (SSLv23_method()); else if (SSLversion & SESOLA_SSLV2 && Sesola_SSLv2_method) SslCtx = SSL_CTX_new (Sesola_SSLv3_method()); else if (SSLversion & SESOLA_SSLV3 && Sesola_SSLv3_method) SslCtx = SSL_CTX_new (Sesola_SSLv3_method()); else if (SSLversion & SESOLA_TLSV1) SslCtx = SSL_CTX_new (TLSv1_method()); else if (SesolaTLS1112) { if (SSLversion & SESOLA_TLSV1_1) SslCtx = SSL_CTX_new (TLSv1_1_method()); else if (SSLversion & SESOLA_TLSV1_2) SslCtx = SSL_CTX_new (TLSv1_2_method()); } if (SslCtx == NULL) { SesolaPrintOpenSslErrorList (); ErrorExitVmsStatus (0, "SSL_CTX_new()", FI_LI); } #else /* SESOLA_BEFORE_110 */ TLSmin = TLSmax = 0; if (SSLversion & SESOLA_TLSV1) TLSmin = TLSmax = TLS1_VERSION; if (SSLversion & SESOLA_TLSV1_1 && !TLSmin) TLSmin = TLS1_1_VERSION; if (SSLversion & SESOLA_TLSV1_1) TLSmax = TLS1_1_VERSION; if (SSLversion & SESOLA_TLSV1_2 && !TLSmin) TLSmin = TLS1_2_VERSION; if (SSLversion & SESOLA_TLSV1_2) TLSmax = TLS1_2_VERSION; #if SESOLA_SINCE_111 if (SSLversion & SESOLA_TLSV1_3 && !TLSmin) TLSmin = TLS1_3_VERSION; if (SSLversion & SESOLA_TLSV1_3) TLSmax = TLS1_3_VERSION; #endif SslCtx = SSL_CTX_new (TLS_method()); if (SslCtx == NULL) { SesolaPrintOpenSslErrorList (); ErrorExitVmsStatus (0, "SSL_CTX_new()", FI_LI); } if (!SSL_CTX_set_min_proto_version (SslCtx, TLSmin)) { SesolaPrintOpenSslErrorList (); ErrorExitVmsStatus (0, "SSL_CTX_set_min_proto_version()", FI_LI); } if (!SSL_CTX_set_max_proto_version (SslCtx, TLSmax)) { SesolaPrintOpenSslErrorList (); ErrorExitVmsStatus (0, "SSL_CTX_set_max_proto_version()", FI_LI); } #endif /* SESOLA_BEFORE_110 */ if (scptr) cptr = scptr->VersionStringPtr = VmGet(sizeof(SesolaVersionString)); else /* this string will be the default config for any service */ cptr = SesolaVersionString; *(sptr = cptr) = '\0'; #if SESOLA_BEFORE_110 if (SSLversion & SESOLA_SSLV2) sptr += sprintf(sptr, "SSLv2"); if (SSLversion & SESOLA_SSLV3) sptr += sprintf(sptr, "%sSSLv3", *cptr ? "," : ""); #endif if (SSLversion & SESOLA_TLSV1) sptr += sprintf(sptr, "%sTLSv1", *cptr ? "," : ""); if (SSLversion & SESOLA_TLSV1_1) sptr += sprintf(sptr, "%sTLSv1.1", *cptr ? "," : ""); if (SSLversion & SESOLA_TLSV1_2) sptr += sprintf(sptr, "%sTLSv1.2", *cptr ? "," : ""); #if SESOLA_SINCE_111 if (SSLversion & SESOLA_TLSV1_3) sptr += sprintf(sptr, "%sTLSv1.3", *cptr ? "," : ""); #endif if (!*cptr) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (SslCtx) { if (scptr) { /* initialising a service */ scptr->SslCtx = SslCtx; scptr->VersionBitmap = SSLversion; scptr->VersionOptions = SSLoptions; } else { /* initialising Secure Sockets capability */ free (SslCtx); /* first from WASD_CONFIG_GLOBAL then second from /SSL= */ if (SSLversion) SesolaVersionBitmap = SSLversion; if (SSLoptions) SesolaVersionOptions = SSLoptions; } } } /*****************************************************************************/ /* Return an integer representing the OpenSSL options bitmask. If the OptionsPtr is provided then use and set this value. */ uint SesolaInitOptions ( char* ConfigString, uint *OptionsPtr ) { uint options; char ch; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInitOptions() !&Z", ConfigString); if (OptionsPtr) options = *OptionsPtr; else options = 0; cptr = ConfigString; while (*cptr) { /* kludge: never see parentheses */ while (*cptr == ',') cptr++; sptr = cptr; while (*cptr && *cptr != ',') cptr++; if (ch = *(zptr = cptr)) *cptr++ = '\0'; if (!*sptr) break; if (strsame(sptr,"0x",2)) /* will fully overwrite and therefore if used then use first */ options = (ulong)strtoul (sptr, NULL, 16); else if (strsame(sptr,"+0x",3)) /* bitwise OR this into the options */ options |= (ulong)strtoul (sptr+1, NULL, 16); else if (strsame(sptr,"-0x",3)) /* bitwise AND this from the options */ options &= ~(ulong)strtoul (sptr+1, NULL, 16); else if (strsame (sptr, "+OP_ALL", -1)) options |= SSL_OP_ALL; else if (strsame (sptr, "-OP_ALL", -1)) options &= ~SSL_OP_ALL; else if (strsame (sptr, "+OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", -1) || strsame (sptr, "+OP_LEG_REN", -1)) options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; else if (strsame (sptr, "-OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", -1) || strsame (sptr, "-OP_LEG_REN", -1)) options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; else if (strsame (sptr, "+OP_CIPHER_SERVER_PREFERENCE", -1)) options |= SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strsame (sptr, "-OP_CIPHER_SERVER_PREFERENCE", -1)) options &= ~SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strsame (sptr, "+OP_LEGACY_SERVER_CONNECT", -1)) options |= SSL_OP_LEGACY_SERVER_CONNECT; else if (strsame (sptr, "-OP_LEGACY_SERVER_CONNECT", -1)) options &= ~SSL_OP_LEGACY_SERVER_CONNECT; else if (strsame (sptr, "+OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", -1) || strsame (sptr, "+OP_NO_SES_RES", -1)) options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; else if (strsame (sptr, "-OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", -1) || strsame (sptr, "-OP_NO_SES_RES", -1)) options &= ~SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; else if (strsame (sptr, "+OP_NO_TICKET", -1)) options |= SSL_OP_NO_TICKET; else if (strsame (sptr, "-OP_NO_TICKET", -1)) options &= ~SSL_OP_NO_TICKET; else if (strsame (sptr, "+OP_SINGLE_DH_USE", -1) || strsame (sptr, "+OP_SING_DH", -1)) options |= SSL_OP_SINGLE_DH_USE; else if (strsame (sptr, "-OP_SINGLE_DH_USE", -1) || strsame (sptr, "-OP_SINGLE_DH", -1)) options &= ~SSL_OP_SINGLE_DH_USE; else if (strsame (sptr, "+OP_TLS_ROLLBACK_BUG", -1)) options |= SSL_OP_TLS_ROLLBACK_BUG; else if (strsame (sptr, "-OP_TLS_ROLLBACK_BUG", -1)) options &= ~SSL_OP_TLS_ROLLBACK_BUG; else if (*sptr != '+' && *sptr != '-' && !strsame(sptr,"0x",2)) FaoToStdout ("-SSL-W-OPTIONS, unknown keyword\n \\!AZ\\\n", sptr); *zptr = ch; } if (OptionsPtr) *OptionsPtr = options; return (options); } /*****************************************************************************/ /* Called during service configuration to initialize an SSL service. SSL services are either created from scratch or if sharing an IP address and port with another SSL service are "cloned". Can also be called to implement server "certificate reload", for which the only approach seems to be to tear-down the existing SSL context and create a completely new one - we'll see! */ BOOL SesolaInitService (SERVICE_STRUCT *svptr) { BOOL CipherListSupplied, KeySupplied, StrictTransSecSupplied, VerifyCAConfigured; int value, LocalOptions, VerifyMode; uint options; ulong ErrorNumber; char *cptr, *sptr; RSA *RsaPtr; SERVICE_STRUCT *tsvptr; SESOLA_CONTEXT *scptr, *ssptr; SSL_CTX *SslCtx; X509 *CertPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInitService() !&X", svptr->SSLserverPtr); FaoToStdout ("%HTTPD-I-SSL, !AZ\n", svptr->ServerHostPort); if (ProtocolHttpsDisabled) { FaoToStdout ("-HTTPD-W-SSL, disabled\n"); return (false); } if (!(scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr)) return (false); if (CliDemo) { scptr->CaFilePtr = scptr->CertFilePtr = scptr->CipherListPtr = scptr->KeyFilePtr = scptr->StrictTransSecPtr = ""; } else { SesolaInitCertFile (svptr); if (scptr->KeyFile[0]) { KeySupplied = true; scptr->KeyFilePtr = scptr->KeyFile; } else { if (scptr->CertFilePtr[0]) { /* service has a specific certificate, assume the key's in that */ scptr->KeyFilePtr = scptr->CertFilePtr; } else if (SesolaDefaultKeyPtr) { /* use the separately specified key */ scptr->KeyFilePtr = SesolaDefaultKeyPtr; } else { /* if no default key then assume it's in the certificate file */ KeySupplied = false; scptr->KeyFilePtr = scptr->CertFilePtr; } } if (scptr->CipherList[0]) { CipherListSupplied = true; scptr->CipherListPtr = scptr->CipherList; } else { if (SesolaDefaultCipherListPtr) { CipherListSupplied = true; scptr->CipherListPtr = SesolaDefaultCipherListPtr; } else { CipherListSupplied = false; scptr->CipherListPtr = ""; } } if (scptr->CaFile[0]) { VerifyCAConfigured = true; scptr->CaFilePtr = scptr->CaFile; } else { if (SesolaDefaultCaFilePtr) { VerifyCAConfigured = true; scptr->CaFilePtr = SesolaDefaultCaFilePtr; } else { VerifyCAConfigured = false; scptr->CaFilePtr = ""; } } if (scptr->CaFilePtr[0]) /* set the global boolean as well (never reset it!) */ VerifyCAConfigured = SesolaVerifyCAConfigured = true; else VerifyCAConfigured = false; if (isdigit(scptr->StrictTransSec[0])) { StrictTransSecSupplied = true; scptr->StrictTransSecPtr = scptr->StrictTransSec; } else { if (SesolaDefaultStrictTransSecPtr && isdigit(*SesolaDefaultStrictTransSecPtr)) { StrictTransSecSupplied = true; scptr->StrictTransSecPtr = SesolaDefaultStrictTransSecPtr; } else { StrictTransSecSupplied = false; scptr->StrictTransSecPtr = ""; } } if (KeySupplied) FaoToStdout ("-SSL-I-KEY, !AZ\n", scptr->KeyFilePtr); if (CipherListSupplied) FaoToStdout ("-SSL-I-CIPHER, !AZ\n", scptr->CipherListPtr); if (VerifyCAConfigured) FaoToStdout ("-SSL-I-CAFILE, !AZ\n", scptr->CaFilePtr); if (scptr->StrictTransSecPtr[0]) FaoToStdout ("-SSL-I-STRICT, HSTS transport security enabled\n"); else FaoToStdout ("-SSL-W-STRICT, HSTS transport security disabled\n"); } SesolaInitContext (scptr->VersionString, svptr->SSLserverPtr); SslCtx = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx; if (scptr->CertFilePtr[0]) value = SSL_CTX_use_certificate_chain_file (SslCtx, scptr->CertFilePtr); else { BIO_reset (SesolaBioMemPtr); BIO_puts (SesolaBioMemPtr, cptr = SesolaMkCert(svptr->ServerHostName)); CertPtr = PEM_read_bio_X509 (SesolaBioMemPtr, NULL, NULL, NULL); if (CertPtr) { value = SSL_CTX_use_certificate (SslCtx, CertPtr); X509_free (CertPtr); } else value = 0; BIO_reset (SesolaBioMemPtr); } if (!value) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } /* combination of SSL version disables and configured discrete options */ if (scptr->VersionOptions) options = scptr->VersionOptions | SesolaSSLoptions; else options = SesolaVersionOptions | SesolaSSLoptions; SesolaPrivateKeyPasswdAttempt = 1; for (;;) { /* set private key password callback */ SSL_CTX_set_default_passwd_cb (SslCtx, &SesolaPrivateKeyPasswd); SSL_CTX_set_default_passwd_cb_userdata (SslCtx, svptr); SesolaPrivateKeyPasswdRequested = false; if (scptr->KeyFilePtr[0]) value = SSL_CTX_use_RSAPrivateKey_file (SslCtx, scptr->KeyFilePtr, SSL_FILETYPE_PEM); else { BIO_reset (SesolaBioMemPtr); BIO_puts (SesolaBioMemPtr, cptr); RsaPtr = PEM_read_bio_RSAPrivateKey (SesolaBioMemPtr, NULL, &SesolaPrivateKeyPasswd, scptr); if (RsaPtr) { value = SSL_CTX_use_RSAPrivateKey (SslCtx, RsaPtr); RSA_free (RsaPtr); } else value = 0; BIO_reset (SesolaBioMemPtr); } /* reset private key password callback */ SSL_CTX_set_default_passwd_cb (SslCtx, NULL); SSL_CTX_set_default_passwd_cb_userdata (SslCtx, svptr); if (value) { if (SesolaPrivateKeyPasswdRequested && OpcomMessages) FaoToOpcom ("%HTTPD-I-PKPASSWD, password accepted"); break; } ErrorNumber = ERR_peek_error (); SesolaPrintOpenSslErrorList (); if (SesolaPrivateKeyPasswdRequested && ERR_GET_LIB(ErrorNumber) == ERR_LIB_EVP && ERR_GET_FUNC(ErrorNumber) == EVP_F_EVP_DECRYPTFINAL_EX && ERR_GET_REASON(ErrorNumber) == EVP_R_BAD_DECRYPT) { if (++SesolaPrivateKeyPasswdAttempt <= SESOLA_PKPASSWD_ATTEMPTS) { /* give the controlling ControlLocalCommand() a chance to notice */ sleep (2); continue; } } if (OpcomMessages) FaoToOpcom ("-SSL-E-PKPASSWD, password not accepted"); scptr->SslCtx = (void*)NULL; return (false); } value = SSL_CTX_check_private_key (SslCtx); if (!value) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } if (*(cptr = scptr->CipherListPtr)) { if (*cptr == '+' || *cptr == '-' || *cptr == '!') { /* modifying the default cipher list */ sptr = VmGet (sizeof(SESOLA_DEFAULT_CIPHER_LIST)+strlen(cptr)+4); sprintf (sptr, "%s :: %s", SESOLA_DEFAULT_CIPHER_LIST, cptr); scptr->CipherListPtr = cptr = sptr; } } else cptr = SESOLA_DEFAULT_CIPHER_LIST; if (!SSL_CTX_set_cipher_list (SslCtx, cptr)) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } if (SesolaTmpDHbitsPtr) { #ifdef SSL_CTX_set_ecdh_auto SSL_CTX_set_ecdh_auto (SslCtx, 1); #endif SSL_CTX_set_tmp_dh_callback (SslCtx, SesolaTmpDHCallback); options |= SSL_OP_CIPHER_SERVER_PREFERENCE; } if (SesolaSessionCacheSize) { if (SesolaSessionCacheSize < SESOLA_DEFAULT_CACHE_RECORD_MAX) SesolaSessionCacheSize = SESOLA_DEFAULT_CACHE_RECORD_MAX; SSL_CTX_set_session_cache_mode (SslCtx, SSL_SESS_CACHE_SERVER); SSL_CTX_sess_set_cache_size (SslCtx, SesolaSessionCacheSize); if (scptr->SessionLifetime) SSL_CTX_set_timeout (SslCtx, scptr->SessionLifetime); else if (SesolaSessionLifetime) SSL_CTX_set_timeout (SslCtx, SesolaSessionLifetime); else SSL_CTX_set_timeout (SslCtx, SesolaSessionCacheTimeout*60); /* inter-process session cache (if multiple per-node "instances") */ if (InstanceNodeConfig > 1) { SSL_CTX_sess_set_new_cb (SslCtx, &SesolaCacheAddRecord); SSL_CTX_sess_set_get_cb (SslCtx, &SesolaCacheFindRecord); SSL_CTX_sess_set_remove_cb (SslCtx, &SesolaCacheRemoveRecord); } } else { SSL_CTX_set_session_cache_mode (SslCtx, SSL_SESS_CACHE_OFF); if (scptr->SessionLifetime) SSL_CTX_set_timeout (SslCtx, scptr->SessionLifetime); else if (SesolaSessionLifetime) SSL_CTX_set_timeout (SslCtx, SesolaSessionLifetime); } if (!(options & SSL_OP_NO_TICKET)) { #ifndef SESOLA_SESSION_TICKET_CALLBACK #define SESOLA_SESSION_TICKET_CALLBACK 1 #endif #if SESOLA_SESSION_TICKET_CALLBACK SSL_CTX_set_tlsext_ticket_key_cb (SslCtx, SesolaSessionTicketCallback); #else SSL_CTX_set_tlsext_ticket_keys (SslCtx, SesolaTicketKey, sizeof(SesolaTicketKey)); #endif } if (scptr->VerifyPeer == SESOLA_VERIFY_PEER_OPTIONAL) VerifyMode = SSL_VERIFY_PEER; else if (scptr->VerifyPeer == SESOLA_VERIFY_PEER_REQUIRED) VerifyMode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; else VerifyMode = SSL_VERIFY_NONE; SSL_CTX_set_verify (SslCtx, VerifyMode, &SesolaCertVerifyCallback); if (scptr->VerifyDepth) SSL_CTX_set_verify_depth (SslCtx, scptr->VerifyDepth); else SSL_CTX_set_verify_depth (SslCtx, SesolaDefaultVerifyDepth); if (scptr->CaFilePtr[0]) { value = SSL_CTX_load_verify_locations (SslCtx, scptr->CaFilePtr, NULL); if (value) value = SSL_CTX_set_default_verify_paths (SslCtx); if (!value) { SesolaPrintOpenSslErrorList (); FaoToStdout ("-SSL-W-VERIFY, peer verification not enabled\n"); scptr->SslCtx = (void*)NULL; return (false); } } /* the session ID ctx needs to be shared between instances or tickets fail */ if (!SSL_CTX_set_session_id_context (SslCtx, SesolaSessionId, sizeof(SesolaSessionId))) { SesolaPrintOpenSslErrorList (); FaoToStdout ("-SSL-W-SESSID, set CTX session ID failed\n"); scptr->SslCtx = (void*)NULL; return (false); } if (SesolaSNI) { if (scptr->VersionBitmap & SESOLA_TLSV1 || scptr->VersionBitmap & SESOLA_TLSV1_1 || #if SESOLA_SINCE_111 scptr->VersionBitmap & SESOLA_TLSV1_2 || scptr->VersionBitmap & SESOLA_TLSV1_3) #else scptr->VersionBitmap & SESOLA_TLSV1_2) #endif { /* enable Server Name Indication */ if (!SSL_CTX_set_tlsext_servername_callback (SslCtx, SesolaSNICallback)) { SesolaSNI = false; FaoToStdout ("%HTTPD-E-SSL, SNI unsupported by SSL library\n"); } else SSL_CTX_set_tlsext_servername_arg (SslCtx, 0); } } #if SESOLA_SINCE_111 if (scptr->VersionBitmap & SESOLA_TLSV1_2 || scptr->VersionBitmap & SESOLA_TLSV1_3) { /* enable Application Level Prototocol Negotiation */ SSL_CTX_set_alpn_select_cb (SslCtx, SesolaALPNCallback, NULL); } #else #if SESOLA_SINCE_102 if (scptr->VersionBitmap & SESOLA_TLSV1_2) { /* enable Application Level Prototocol Negotiation */ SSL_CTX_set_alpn_select_cb (SslCtx, SesolaALPNCallback, NULL); } #endif #endif SSL_CTX_set_options (SslCtx, options); return (true); } /*****************************************************************************/ /* Return the number of TLS services in the global list. */ int SesolaServiceCount () { int count; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ count = 0; LIST_ITERATE (svptr, &ServiceList) if (svptr->SchemeType == SCHEME_HTTPS) count++; return (count); } /*****************************************************************************/ /* Command-line service certificate reload function. It seems to be able to be done with impunity and as often as might be necessary. In-progress TLS requests continue unaffected. Hmmm. Too good to be true? Perhaps. Of course, OpenSSL objects, including the likes of service "contexts" and connections in-progress, are reference-counted and so can be free()d while in use, with resources returned to pools when the references drop to zero. In the case of memory, this may not reappear as unused until some time after the reset. */ void SesolaControlReloadCerts () { int ErrorCount, SSLcount, ReloadCount; char *cptr; SESOLA_CONTEXT *scptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaControlReloadCerts()"); /* enable SYSPRV to allow access to protected (certificate) files */ sys$setprv (1, &SysPrvMask, 0, 0); ErrorCount = SSLcount = ReloadCount = 0; LIST_ITERATE (svptr, &ServiceList) { if (svptr->SchemeType != SCHEME_HTTPS) continue; SSLcount++; scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr; if (!scptr) continue; if (!scptr->SslCtx) continue; SSL_CTX_free ((SSL_CTX*)scptr->SslCtx); scptr->SslCtx = NULL; if (SesolaInitService (svptr)) ReloadCount++; else ErrorCount++; } sys$setprv (0, &SysPrvMask, 0, 0); if (WATCH_CATEGORY (WATCH_INTERNAL)) WatchThis (WATCHALL, WATCH_INTERNAL, "SSL (re)load !UL of !UL SSL certificates!&@", ReloadCount, SSLcount, ErrorCount ? ", !UL error!%s" : "", ErrorCount); FaoToStdout ("%HTTPD-I-SSL, \ (re)load !UL of !UL SSL certificates!&@\n", ReloadCount, SSLcount, ErrorCount ? ", !UL error!%s" : "", ErrorCount); if (OpcomMessages & OPCOM_HTTPD || OpcomMessages & OPCOM_AUTHORIZATION) FaoToOpcom ("%HTTPD-I-SSL, \ (re)load !UL of !UL SSL certificates!&@", ReloadCount, SSLcount, ErrorCount ? ", !UL error!%s" : "", ErrorCount); } /*****************************************************************************/ /* Parse the (potentially wildcarded) certificate file specification into an actual certificate file name. The token ':*' inserts the port, and '*' inserts the service host name (ODS-5 compatible, to make it ODS-2 compatible use '**'). Any other character is inserted as-is. Examples: wasd_root:[local]*_bundle.crt -> wasd_root:[local]the^.host^.name_bundle.crt wasd_root:[local]*_:*_bundle.crt -> wasd_root:[local]the^.host^.name_443_bundle.crt wasd_root:[local]**_bundle.crt -> wasd_root:[local]the_host_name_bundle.crt */ void SesolaInitCertFile (SERVICE_STRUCT *svptr) { BOOL NameHit, PortHit; char CertFile [256]; char *aptr, *cptr, *sptr, *zptr; SESOLA_CONTEXT *scptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInitCertFile()"); if (!(scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr)) return; if (scptr->CertFile[0]) scptr->CertFilePtr = scptr->CertFile; else if (SesolaDefaultCertPtr) scptr->CertFilePtr = SesolaDefaultCertPtr; else scptr->CertFilePtr = ""; if (strchr(scptr->CertFilePtr,'*')) { NameHit = PortHit = false; zptr = (sptr = CertFile) + sizeof(CertFile)-1; cptr = scptr->CertFilePtr; while (*cptr && sptr < zptr) { if (NameHit && !PortHit && *(ushort*)cptr == ':*') { PortHit = true; cptr += 2; for (aptr = svptr->ServerPortString; *aptr && sptr < zptr; *sptr++ = *aptr++); } else if (!NameHit && *cptr == '*') { NameHit = true; cptr++; if (*cptr == '*') { /* ODS-2 compatibility */ cptr++; for (aptr = svptr->ServerHostName; *aptr && sptr < zptr; aptr++) if (*aptr == '.') *sptr++ = '_'; else *sptr++ = *aptr; } else { /* ODS-5 compatibility */ for (aptr = svptr->ServerHostName; *aptr && sptr < zptr; *sptr++ = *aptr++); } } else *sptr++ = *cptr++; } *sptr = '\0'; scptr->CertFilePtr = VmGet (sptr - CertFile + 1); memcpy (scptr->CertFilePtr, CertFile, sptr - CertFile); } FaoToStdout ("-SSL-I-CERT, !AZ\n", scptr->CertFilePtr && scptr->CertFilePtr[0] ? scptr->CertFilePtr : "none - make ex nihilo"); } /*****************************************************************************/ /* Configure the basics of the SSL client service (i.e. can originate outgoing SSL sessions with SSL servers). Used by the HTTP-SSL proxy (gateway) service. */ BOOL SesolaInitClientService (SERVICE_STRUCT *svptr) { BOOL CipherListSupplied, KeySupplied; int value; ulong ErrorNumber; char *cptr, *sptr; RSA *RsaPtr; SSL_CTX *SslCtx; SESOLA_CONTEXT *scptr; X509 *CertPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaInitClientService()"); if (ProtocolHttpsDisabled) { FaoToStdout ("-HTTPD-W-SSL, disabled\n"); return (false); } if (!svptr->SSLclientPtr) { /* no client-specific context, use any associated server context */ if (svptr->SSLserverPtr) { svptr->SSLclientPtr = VmGet (sizeof(SESOLA_CONTEXT)); memcpy (svptr->SSLclientPtr, svptr->SSLserverPtr, sizeof(SESOLA_CONTEXT)); } } if (!(scptr = (SESOLA_CONTEXT*)svptr->SSLclientPtr)) return (false); if (scptr->CertFile[0]) scptr->CertFilePtr = scptr->CertFile; else if (SesolaDefaultCertPtr) scptr->CertFilePtr = SesolaDefaultCertPtr; else scptr->CertFilePtr = ""; if (scptr->KeyFile[0]) { KeySupplied = true; scptr->KeyFilePtr = scptr->KeyFile; } else { if (scptr->CertFile[0]) { /* service has a specific certificate, assume the key's in that */ scptr->KeyFilePtr = scptr->CertFile; } else if (SesolaDefaultKeyPtr) { /* use the separately specified key */ scptr->KeyFilePtr = SesolaDefaultKeyPtr; } else { /* if no default key then assume it's in the certificate file */ KeySupplied = false; scptr->KeyFilePtr = scptr->CertFilePtr; } } if (scptr->CipherList[0]) { CipherListSupplied = true; scptr->CipherListPtr = scptr->CipherList; } else { if (SesolaDefaultCipherListPtr) { CipherListSupplied = true; scptr->CipherListPtr = ""; } else { CipherListSupplied = false; scptr->CipherListPtr = ""; } } if (scptr->CaFile[0]) scptr->CaFilePtr = scptr->CaFile; else if (SesolaDefaultCaFilePtr) scptr->CaFilePtr = SesolaDefaultCaFilePtr; else scptr->CaFilePtr = ""; if (CipherListSupplied) FaoToStdout ("-SSL-I-CIPHER, (a/client) !AZ\n", scptr->CipherListPtr); if (scptr->VerifyCA) FaoToStdout ("-SSL-I-CAFILE, (a/client) !AZ\n", scptr->CaFilePtr); SesolaInitContext (scptr->VersionString, svptr->SSLclientPtr); SslCtx = ((SESOLA_CONTEXT*)svptr->SSLclientPtr)->SslCtx; FaoToStdout ("-SSL-I-PROTOCOL, (a/client) version !AZ\n", scptr->VersionStringPtr); if (scptr->CertFilePtr[0]) value = SSL_CTX_use_certificate_file (SslCtx, scptr->CertFilePtr, SSL_FILETYPE_PEM); else { BIO_reset (SesolaBioMemPtr); BIO_puts (SesolaBioMemPtr, cptr = SesolaMkCert(svptr->ServerHostName)); CertPtr = PEM_read_bio_X509 (SesolaBioMemPtr, NULL, NULL, NULL); if (CertPtr) { value = SSL_CTX_use_certificate (SslCtx, CertPtr); X509_free (CertPtr); } else value = 0; BIO_reset (SesolaBioMemPtr); } if (!value) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } SesolaPrivateKeyPasswdAttempt = 1; for (;;) { /* set private key password callback */ SSL_CTX_set_default_passwd_cb (SslCtx, &SesolaPrivateKeyPasswd); SSL_CTX_set_default_passwd_cb_userdata (SslCtx, svptr); SesolaPrivateKeyPasswdRequested = false; if (scptr->KeyFilePtr[0]) value = SSL_CTX_use_RSAPrivateKey_file (SslCtx, scptr->KeyFilePtr, SSL_FILETYPE_PEM); else { BIO_reset (SesolaBioMemPtr); BIO_puts (SesolaBioMemPtr, cptr); RsaPtr = PEM_read_bio_RSAPrivateKey (SesolaBioMemPtr, NULL, &SesolaPrivateKeyPasswd, scptr); if (RsaPtr) { value = SSL_CTX_use_RSAPrivateKey (SslCtx, RsaPtr); RSA_free (RsaPtr); } else value = 0; BIO_reset (SesolaBioMemPtr); } /* reset private key password callback */ SSL_CTX_set_default_passwd_cb (SslCtx, NULL); SSL_CTX_set_default_passwd_cb_userdata (SslCtx, svptr); if (value) { if (SesolaPrivateKeyPasswdRequested && OpcomMessages) FaoToOpcom ("%HTTPD-I-PKPASSWD, password accepted"); break; } ErrorNumber = ERR_peek_error (); SesolaPrintOpenSslErrorList (); if (SesolaPrivateKeyPasswdRequested && ERR_GET_LIB(ErrorNumber) == ERR_LIB_EVP && ERR_GET_FUNC(ErrorNumber) == EVP_F_EVP_DECRYPTFINAL_EX && ERR_GET_REASON(ErrorNumber) == EVP_R_BAD_DECRYPT) { if (++SesolaPrivateKeyPasswdAttempt <= SESOLA_PKPASSWD_ATTEMPTS) { /* give the controlling ControlLocalCommand() a chance to notice */ sleep (2); continue; } } if (OpcomMessages) FaoToOpcom ("-SSL-E-PKPASSWD, password not accepted"); scptr->SslCtx = (void*)NULL; return (false); } value = SSL_CTX_check_private_key (SslCtx); if (!value) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } if (*(cptr = scptr->CipherListPtr)) { if (*cptr == '+' || *cptr == '-' || *cptr == '!') { /* modifying the default cipher list */ sptr = VmGet (sizeof(SESOLA_DEFAULT_CIPHER_LIST)+strlen(cptr)+4); sprintf (sptr, "%s :: %s", SESOLA_DEFAULT_CIPHER_LIST, cptr); scptr->CipherListPtr = cptr = sptr; } } else cptr = SESOLA_DEFAULT_CIPHER_LIST; if (!SSL_CTX_set_cipher_list (SslCtx, cptr)) { SesolaPrintOpenSslErrorList (); scptr->SslCtx = (void*)NULL; return (false); } if (SesolaSessionCacheSize) { if (SesolaSessionCacheSize < SESOLA_DEFAULT_CACHE_RECORD_MAX) SesolaSessionCacheSize = SESOLA_DEFAULT_CACHE_RECORD_MAX; SSL_CTX_set_session_cache_mode (SslCtx, SSL_SESS_CACHE_SERVER); SSL_CTX_sess_set_cache_size (SslCtx, SesolaSessionCacheSize); SSL_CTX_set_timeout (SslCtx, SesolaSessionCacheTimeout*60); /* inter-process session cache (if multiple per-node "instances") */ if (InstanceNodeConfig > 1) { SSL_CTX_sess_set_new_cb (SslCtx, &SesolaCacheAddRecord); SSL_CTX_sess_set_get_cb (SslCtx, &SesolaCacheFindRecord); SSL_CTX_sess_set_remove_cb (SslCtx, &SesolaCacheRemoveRecord); } } else SSL_CTX_set_session_cache_mode (SslCtx, SSL_SESS_CACHE_OFF); if (scptr->VerifyCA) SSL_CTX_set_verify (SslCtx, SSL_VERIFY_CLIENT_ONCE, &SesolaCertVerifyCallback); else SSL_CTX_set_verify (SslCtx, SSL_VERIFY_NONE, &SesolaCertVerifyCallback); SSL_CTX_set_verify_depth (SslCtx, SesolaDefaultVerifyDepth); if (scptr->CaFilePtr[0]) { value = SSL_CTX_load_verify_locations (SslCtx, scptr->CaFilePtr, NULL); if (value) value = SSL_CTX_set_default_verify_paths (SslCtx); if (!value) { SesolaPrintOpenSslErrorList (); FaoToStdout ("-SSL-W-SSL, (a/client) CA verification not enabled\n"); scptr->SslCtx = (void*)NULL; return (false); } } /* combination of SSL version disables and configured discrete options */ if (scptr->VersionOptions) SSL_CTX_set_options (SslCtx, (scptr->VersionOptions | SesolaSSLoptions)); else SSL_CTX_set_options (SslCtx, (SesolaVersionOptions | SesolaSSLoptions)); return (true); } /*****************************************************************************/ /* If the private key in WASD_SSL_CERT, WASD_SSL_KEY, or the equivalent, does not contain an embedded password (which is by far less "secure" against key stealing) the private key callback set above results in this function being called. Output a message to the console, to the monitor status message buffer, and optionally if enabled to OPCOM, requesting that a password be manually supplied. The password is input at the command line using the control /DO=SSL=KEY=PASSWORD directive, which prompts for the password and then writes it into the global section shared memory control directive buffer. This function continues to poll that buffer at one second intervals looking for the indicator of the password. When found it copies it into the 'PasswdBuffer' and returns the length. If not supplied it eventually times-out resulting in the startup being cancelled (which should then restart). Note that POLLING MUST BE USED because user-mode ASTs are disabled during service (network) initialization, rendering lock delivery and the like possibilities of notification unusable (at this mode) during this period. */ int SesolaPrivateKeyPasswd ( char *PasswdBuffer, int SizeOfPasswdBuffer, int UnknownParam, void *UserData ) { int cnt, PasswdLength; char *cptr, *sptr, *zptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaPrivateKeyPasswd() !UL !UL", SizeOfPasswdBuffer, UserData); /* indicates a private key password was required for this service */ SesolaPrivateKeyPasswdRequested = true; svptr = (SERVICE_STRUCT*)UserData; /* ensure we start with a nice empty buffer */ memset (HttpdGblSecPtr->PkPasswd, 0, sizeof(HttpdGblSecPtr->PkPasswd)); PasswdBuffer[0] = '\0'; /* wait for the password to magically appear!! (crude but effective) */ for (cnt = SESOLA_PKPASSWD_REQUEST_SECONDS; cnt; cnt--) { if (!(cnt % 60)) { FaoToStdout ("%HTTPD-I-PKPASSWD, \ !20%D, request !UL/!UL for private key password, !AZ//!AZ\n", 0, SesolaPrivateKeyPasswdAttempt, SESOLA_PKPASSWD_ATTEMPTS, svptr->SchemeNamePtr, svptr->ServerHostPort); FaoToBuffer (HttpdGblSecPtr->StatusMessage, sizeof(HttpdGblSecPtr->StatusMessage), NULL, "%HTTPD-I-PKPASSWD, !20%D, \ !AZ request !UL/!UL for private key password, !AZ//!AZ", 0, InstanceNodeCurrent > 1 ? HttpdProcess.PrcNam : "", SesolaPrivateKeyPasswdAttempt, SESOLA_PKPASSWD_ATTEMPTS, svptr->SchemeNamePtr, svptr->ServerHostPort); /* if any OPCOM at all is enabled then output this message */ if (OpcomMessages) FaoToOpcom ("%HTTPD-I-PKPASSWD, \ request !UL/!UL for private key password, !AZ//!AZ", SesolaPrivateKeyPasswdAttempt, SESOLA_PKPASSWD_ATTEMPTS, svptr->SchemeNamePtr, svptr->ServerHostPort); } sleep (1); /* if a password was supplied whilst sleeping */ if (HttpdGblSecPtr->PkPasswd[0]) break; } HttpdGblSecPtr->StatusMessage[0] = '\0'; if (!cnt) { /* timed-out waiting for a response */ ControlMessage ("timeout waiting for private key password"); sleep (1); exit (SS$_CANCEL); } zptr = (sptr = PasswdBuffer) + SizeOfPasswdBuffer; for (cptr = HttpdGblSecPtr->PkPasswd; *cptr && sptr < zptr; *sptr++ = *cptr++); /* overflow just cancels the password */ if (sptr > zptr) sptr = PasswdBuffer; *sptr = '\0'; PasswdLength = sptr - PasswdBuffer; for (sptr = PasswdBuffer; *sptr && isspace(*sptr); sptr++); if (!*sptr && sptr > PasswdBuffer) { /* password comprising all spaces is used to abort the startup */ ControlMessage ("empty private key password, aborting startup"); /* just exit the process */ ExitStatus = SS$_ABORT; HttpdExit (&ExitStatus); /* cancel any startup messages provided for the monitor */ HttpdGblSecPtr->StatusMessage[0] = '\0'; sys$delprc (0, 0); } /* remove the actual password from the control buffer */ memset (HttpdGblSecPtr->PkPasswd, 0, sizeof(HttpdGblSecPtr->PkPasswd)); ControlMessage ("password received"); return (PasswdLength); } /*****************************************************************************/ /* Generate fresh random data used with the session ticket key and session ID context. As the ticket key name (first 16 bytes) will be public make the session ID the same data. Return a pointer to the key data. */ uchar* SesolaSessionTicketNewKey () { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSessionTicketNewKey()"); RAND_bytes (SesolaTicketKey, sizeof(SesolaTicketKey)); memmove (SesolaSessionId, SesolaTicketKey, sizeof(SesolaSessionId)); return (SesolaTicketKey); } /*****************************************************************************/ /* Propagate supplied session ticket key data to global storage and applicable services. Return a pointer to the key data. The ticket random data is also used (with offset) to provide the session ID context. The session ID context must be common across instances for session ticket reuse to be successful. As the ticket key name (first 16 bytes) will be public make the session ID the same data. Can be directly called by InstanceSessionTicketKey() and (indirectly) by ControlHttpdAst() when being propagated over multiiple instances (including cluster-wide). */ uchar* SesolaSessionTicketUseKey (uchar *keyptr) { static unsigned long TwoMinsDelta [2] = { -1200000000, -1 }; int idx; ulong options; SERVICE_STRUCT *svptr; SSL_CTX *SslCtx; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSessionTicketUseKey()"); if (WATCH_CATEGORY(WATCH_SESOLA)) { WatchThis (WATCHALL, WATCH_SESOLA, "SESSION TICKET key"); WatchDataDump (keyptr, sizeof(SesolaTicketKey)); } /* null-terminated string then 48 bytes of session key */ if (strsame (keyptr, CONTROL_SESSION_TICKET_KEY, -1)) keyptr += sizeof(CONTROL_SESSION_TICKET_KEY); /* else no null-terminated string, just the 48 bytes of session key */ /* note the date-day the key was refreshed by InstanceSupervisor() */ SesolaTicketKeySuperDay = HttpdNumTime[2]; /* time of this most recent refresh */ PUT_QUAD_QUAD (&HttpdTime64, &SesolaTicketKeyTime64); /* if the key is empty (16 byte lock value block) */ if (!*(ULONGPTR)keyptr) keyptr = SesolaSessionTicketNewKey(); memmove (SesolaTicketKey, keyptr, sizeof(SesolaTicketKey)); memmove (SesolaSessionId, keyptr, sizeof(SesolaSessionId)); /* propagate session ticket key to required services */ LIST_ITERATE (svptr, &ServiceList) { if (svptr->SchemeType != SCHEME_HTTPS) continue; if (SslCtx = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx) { options = SSL_CTX_get_options (SslCtx); if (!(options & SSL_OP_NO_TICKET)) { #if !SESOLA_SESSION_TICKET_CALLBACK SSL_CTX_set_tlsext_ticket_keys (SslCtx, SesolaTicketKey, sizeof(SesolaTicketKey)); #endif /* the session ID ctx also needs to be shared between instances */ SSL_CTX_set_session_id_context (SslCtx, SesolaSessionId, sizeof(SesolaSessionId)); } } } return (SesolaTicketKey); } /*****************************************************************************/ /* Session tickets, defined in RFC5077, provide an enhanced session resumption capability where the server implementation is not required to maintain per session state. The callback function will be called for every client instigated TLS session when session ticket extension is presented in the TLS hello message. It is the responsibility of this function to create or retrieve the cryptographic parameters and to maintain their state. This function, in conjunction with SesolaSessionTicketNewKey(), SesolaSessionTicketUseKey() and InstanceSessionTicketKey() manage coherent usage of the session ticket keys allowing multi-instance, cluster-wide if required, session resumption using tickets, and refreshes those ticket keys once a day. */ #if SESOLA_SESSION_TICKET_CALLBACK int SesolaSessionTicketCallback ( SSL *SslPtr, uchar *KeyName, uchar *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc ) { #ifndef tlsext_tick_md #ifdef OPENSSL_NO_SHA256 #define tlsext_tick_md EVP_sha1 #else #define tlsext_tick_md EVP_sha256 #endif #endif static uchar *AesKeyPtr = SesolaTicketKey + 32, *HmacKeyPtr = SesolaTicketKey + 16, *NameKeyPtr = SesolaTicketKey; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSessionTicketCallback() !UL !16&H", enc, KeyName); if (enc) { /******************/ /* create session */ /******************/ memcpy (KeyName, NameKeyPtr, 16); RAND_bytes (iv, 16); if (!EVP_EncryptInit_ex (ectx, EVP_aes_128_cbc(), NULL, AesKeyPtr, iv)) return (-1); if (!HMAC_Init_ex (hctx, HmacKeyPtr, 16, tlsext_tick_md(), NULL)) return (-1); SesolaSessionTicketNew++; } else { /********************/ /* retrieve session */ /********************/ if (memcmp (KeyName, NameKeyPtr, 16)) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "!16&H !!= !16&H", KeyName, NameKeyPtr); SesolaSessionTicketMiss++; return (0); } if (!HMAC_Init_ex (hctx, HmacKeyPtr, 16, tlsext_tick_md(), NULL)) return (-1); if (!EVP_DecryptInit_ex (ectx, EVP_aes_128_cbc(), NULL, AesKeyPtr, iv)) return (-1); SesolaSessionTicketHit++; } return (1); } #endif /* SESOLA_SESSION_TICKET_CALLBACK */ /*****************************************************************************/ /* Temporary DH params supporting ephemeral keys (for "perfect forward security"). See explanation in module prologue under section "FORWARD SECRECY". */ DH* SesolaTmpDHCallback ( SSL *SslPtr, int IsExport, int KeyLength ) { #define BITS_MAX 5 /* future proofing? :-} */ static int bits [BITS_MAX] = { 512, 1024, 2048, 4096, 8192 }; static DH *bitdh [BITS_MAX]; static int maxidx; static ulong PrevTickSecond; int keybit, idx; char buf [256]; DH *dhptr; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaTmpDHCallback() !UL", KeyLength); if (!SslPtr) { /**************/ /* initialise */ /**************/ char *sptr; /* enable SYSPRV allowing access to should-be protected files */ sys$setprv (1, &SysPrvMask, 0, 0); for (idx = 0; idx < BITS_MAX; idx++) bitdh[idx] = NULL; for (idx = 0; idx < BITS_MAX; idx++) { FILE *pfile; sprintf (buf, "WASD_ROOT:[LOCAL]DH_PARAM_%d.PEM", bits[idx]); if ((pfile = fopen (buf, "r")) == NULL) continue; bitdh[idx] = PEM_read_DHparams (pfile, NULL, NULL, NULL); fclose (pfile); maxidx = idx + 1; } sys$setprv (0, &SysPrvMask, 0, 0); if (maxidx) { *(sptr = buf) = '\0'; for (idx = 0; idx < maxidx; idx++) if (bitdh[idx] != NULL) sptr += sprintf (sptr, "%s%d", buf[0] ? "," : "", bits[idx]); strcpy (sptr, " bits"); SesolaTmpDHbitsPtr = VmGet(strlen(buf)+1); strcpy (SesolaTmpDHbitsPtr, buf); FaoToStdout ("-SSL-I-DH, ephemeral DH param !AZ\n", buf); } else FaoToStdout ("-SSL-W-DH, no ephemeral DH param\n"); return ((DH*)SesolaTmpDHbitsPtr); } /************/ /* callback */ /************/ rqptr = (REQUEST_STRUCT*)SSL_get_app_data (SslPtr); keybit = 0; dhptr = NULL; for (idx = 0; idx < maxidx; idx++) { if (bitdh[idx] == NULL) continue; if (bits[idx] >= KeyLength) { if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "DH key requested !UL got !UL bits", KeyLength, bits[idx]); return (bitdh[idx]); } keybit = bits[idx]; dhptr = bitdh[idx]; } if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "DH key requested !UL got !UL bits", KeyLength, dhptr ? keybit : 0); return (dhptr); } /*****************************************************************************/ /* Implement Application-Layer Protocol Negotiation (ALPN), a TLS extension. ALPN allows the application layer to negotiate which protocol should be performed over a secure connection in a manner which avoids additional round trips and which is independent of the application layer protocols. */ int SesolaALPNCallback ( SSL *SslPtr, uchar **out, uchar *outlen, uchar *in, uint inlen, void *arg ) { BOOL h2, http11, tls01; uint cnt, retval; char *cptr, *sptr, *zptr; REQUEST_STRUCT *rqptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = (SESOLA_STRUCT*)SSL_get_ex_data (SslPtr, 0); rqptr = (REQUEST_STRUCT*)sesolaptr->RequestPtr; h2 = http11 = tls01 = false; /* vector of 8-bit, length prefixed byte strings */ for (cptr = in; cptr < in + inlen; cptr += cnt) { cnt = *cptr++; if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "ALPN !#AZ", cnt, cptr); if (cnt == 2 && MATCH2 (cptr, "h2")) h2 = true; if (cnt == 8 && MATCH8 (cptr, "http/1.1")) http11 = true; if (cnt == 11 && MATCH11 (cptr, "tls-alpn-01")) tls01 = true; } retval = SSL_TLSEXT_ERR_NOACK; if (tls01) { *out = sesolaptr->ALPNok = "tls-alpn-01"; *outlen = 11; retval = SSL_TLSEXT_ERR_OK; } if (retval == SSL_TLSEXT_ERR_NOACK) { if (h2 && Http2Enabled && rqptr->ServicePtr->Http2Enabled) { *out = sesolaptr->ALPNok = "h2"; *outlen = 2; retval = SSL_TLSEXT_ERR_OK; } } if (retval == SSL_TLSEXT_ERR_NOACK) { if (http11) { *out = sesolaptr->ALPNok = "http/1.1"; *outlen = 8; retval = SSL_TLSEXT_ERR_OK; } } if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "OK !AZ", sesolaptr->ALPNok ? sesolaptr->ALPNok : "(none)"); return (retval); } /*****************************************************************************/ /* Implement TLS1 Server Name Indication (SNI) callback (from RFC 4366). Searches the SSL service list for a matching name (certificates are issued against a host name, not against a name:port combination). The first name matched results in the SSL context being set/changed. Then continue and set the virtual service if available (significantly more efficient than doing it using ServiceFindVirtual() later). */ int SesolaSNICallback ( SSL *SslPtr, int *UnusedArg1, void *UnusedArg2 ) { BOOL wwwName; int snlen, ServerPort, VerifyDepth, VerifyMode; char *cptr, *sptr, *snptr, *zptr; REQUEST_STRUCT *rqptr; SERVICE_STRUCT *svptr; SESOLA_STRUCT *sesolaptr; SSL_CTX *SslCtx; X509_STORE_CTX *VerifyCallback; /*********/ /* begin */ /*********/ sesolaptr = (SESOLA_STRUCT*)SSL_get_ex_data (SslPtr, 0); snptr = SSL_get_servername (SslPtr, TLSEXT_NAMETYPE_host_name); if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaSNICallback() !&Z", snptr); if (!snptr) return (SSL_TLSEXT_ERR_OK); if (ServiceWwwImplied) wwwName = SAME4(snptr,'www.'); else wwwName = false; rqptr = (REQUEST_STRUCT*)sesolaptr->RequestPtr; /* note the original service port number */ ServerPort = rqptr->ServicePtr->ServerPort; /* used to emulate the Apache SSL_TLS_SNI variable */ zptr = (sptr = sesolaptr->SNIServerName) + sizeof(sesolaptr->SNIServerName)-1; for (cptr = snptr; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; snlen = cptr - snptr; LIST_ITERATE (svptr, &ServiceList) { if (svptr->SchemeType != SCHEME_HTTPS) continue; if (!((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx) continue; if (!wwwName || snlen != svptr->ServerHostNameLength + 4) if (snlen != svptr->ServerHostNameLength) continue; /* match the SNI name to the service name */ sptr = snptr; if (wwwName && !MATCH4 (sptr, "www.")) sptr += 4; cptr = svptr->ServerHostName; if (snlen >= 8) { if (!MATCH8 (sptr, cptr)) continue; sptr += 8; cptr += 8; } while (*cptr && *sptr && *cptr == *sptr) { cptr++; sptr++; } if (*sptr || *cptr) continue; /* match to the original port */ if (ServerPort != svptr->ServerPort) continue; /* name+port match! */ if (!sesolaptr->SNIctxSet) { /* set the SSL context */ SSL_set_SSL_CTX (SslPtr, ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx); sesolaptr->SNIctxSet = true; /* propagate newly set context client verify to SSL-specific */ SslCtx = SSL_get_SSL_CTX (SslPtr); VerifyMode = SSL_CTX_get_verify_mode (SslCtx); VerifyDepth = SSL_CTX_get_verify_depth (SslCtx); VerifyCallback = SSL_CTX_get_verify_callback (SslCtx); SSL_set_verify (SslPtr, VerifyMode, VerifyCallback); SSL_set_verify_depth (SslPtr, VerifyDepth); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "SNI match !AZ,!UL", snptr, svptr->ServerPort); } /* change to this service */ rqptr->ServicePtr = svptr; sesolaptr->NetIoPtr->ServicePtr = svptr; sesolaptr->SNIserviceSet = true; if (WATCHPNT(rqptr)) { if (WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "SNI VIRTUAL !AZ", svptr->ServerHostPort); else if (WATCH_CATEGORY(WATCH_CONNECT)) WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "VIRTUAL (SNI) !AZ", svptr->ServerHostPort); } break; } if (WATCHING (rqptr, WATCH_SESOLA)) if (!sesolaptr->SNIctxSet) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "SNI FAIL !AZ", snptr); return (SSL_TLSEXT_ERR_OK); } /*****************************************************************************/ /* Return a TLS Application Level Protocol Negotiation string or NULL. */ char* SesolaRequestALPN (REQUEST_STRUCT *rqptr) { SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestALPN()"); if (!(sesolaptr = SesolaRequestSesolaPtr (rqptr))) return (NULL); return (sesolaptr->ALPNok); } /*****************************************************************************/ /* Return a request's secure socket pointer or NULL. */ void* SesolaRequestSesolaPtr (REQUEST_STRUCT *rqptr) { SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestSesolaPtr()"); if (!rqptr) return (NULL); sesolaptr = NULL; if (HTTP2_REQUEST(rqptr)) sesolaptr = (SESOLA_STRUCT*)rqptr-> Http2Stream.Http2Ptr->NetIoPtr->SesolaPtr; if (!sesolaptr) sesolaptr = (SESOLA_STRUCT*)rqptr->NetIoPtr->SesolaPtr; return (sesolaptr); } /*****************************************************************************/ /* Return the value for ServiceFindVirtual() to use. This stub function just used to dereference (void*). */ BOOL SesolaSNIserviceSet (SESOLA_STRUCT *sesolaptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSNIserviceSet() !&B", sesolaptr->SNIserviceSet); return (sesolaptr->SNIserviceSet); } /*****************************************************************************/ /* Called by WatchSetWatch(). */ int SesolaGetWatch (SESOLA_STRUCT *sesolaptr) { return (sesolaptr->WatchItem); } /*****************************************************************************/ /* Called by WatchSetWatch(). */ void SesolaSetWatch ( SESOLA_STRUCT *sesolaptr, int item ) { sesolaptr->WatchItem = item; sesolaptr->NetIoPtr->WatchItem = item; } /*****************************************************************************/ /* Print out the OpenSSL error list. For reporting errors associated with certificate loading. Based on [.CRYPTO.ERR]ERR_PRN.C. The error number (based on [.CRYPTO.ERR]ERR.H ERR_PACK macro) is a bit-vector comprising 31..24 (8 bits) the library code, 23..12 (12 bits) the function code 11..0 (12 bits) the reason code. A fatal error is the decimal value 32 (bit 6) and is used in various bit-wise operations. */ SesolaPrintOpenSslErrorList () { int LineNumber, Flags; ulong ErrorNumber; char *FileNamePtr, *DataStringPtr; char Buffer [256]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaPrintOpenSslErrorList()"); while (ErrorNumber = ERR_get_error_line_data (&FileNamePtr, &LineNumber, &DataStringPtr, &Flags)) { FaoToStdout ( "-SSL-W-ERROR, \"!AZ\" (!AZ !SL)!&?\"\r\r!AZ!&?\"\r\r\n", ERR_error_string (ErrorNumber, Buffer), FileNamePtr, LineNumber, ((Flags & ERR_TXT_STRING)), ((Flags & ERR_TXT_STRING) ? DataStringPtr : ""), ((Flags & ERR_TXT_STRING))); } } /*****************************************************************************/ /* Provides OpenSSL status information at various states of SSL processing via the WATCH facility. */ #if WATCH_CAT SesolaWatchInfoCallback ( SSL *SslPtr, int WhereExactly, int value ) { int item; char *cptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaWatchInfoCallback() !UL !SL", WhereExactly, value); sesolaptr = (SESOLA_STRUCT*)SSL_get_ex_data (SslPtr, 0); if (!sesolaptr->WatchItem) return; switch (WhereExactly & SSL_ST_MASK) { case SSL_CB_LOOP : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "LOOP !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_EXIT : if (value == 0) WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "EXIT alert failed in !AZ", SSL_state_string_long(SslPtr)); else if (value < 0) WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "EXIT error/blocking in !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_READ : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "READ read !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_WRITE : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "WRITE !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_ALERT : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "ALERT !AZ !AZ:!AZ", (WhereExactly & SSL_CB_READ) ? "read" : "write", SSL_alert_type_string_long(value), SSL_alert_desc_string_long(value)); break; case SSL_CB_READ_ALERT : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "READ ALERT !AZ:!AZ", SSL_alert_type_string_long(value), SSL_alert_desc_string_long(value)); break; case SSL_CB_WRITE_ALERT : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "WRITE ALERT !AZ:!AZ", SSL_alert_type_string_long(value), SSL_alert_desc_string_long(value)); break; case SSL_CB_ACCEPT_LOOP : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "ACCEPT LOOP !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_ACCEPT_EXIT : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "ACCEPT EXIT !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_CONNECT_LOOP : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "CONNECT LOOP !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_CONNECT_EXIT : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "CONNECT EXIT !AZ", SSL_state_string_long(SslPtr)); break; case SSL_CB_HANDSHAKE_START : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "START handshake"); break; case SSL_CB_HANDSHAKE_DONE : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "DONE handshake"); break; default : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "!&X !AZ", WhereExactly, SSL_state_string_long(SslPtr)); } } #endif /* WATCH_CAT */ /*****************************************************************************/ /* Provides OpenSSL Input/Output information each time an SSL read or write occurs via the WATCH facility. These macros can be found in [.CRYPTO.BIO]BIO.H */ int SesolaWatchBioCallback ( BIO *bioptr, int cmd, char *argp, int argi, long argl, long retval ) { #if WATCH_CAT SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = (SESOLA_STRUCT*)BIO_get_callback_arg (bioptr); if (!sesolaptr->WatchItem) return (retval); if (WATCH_MODULE(WATCH_MOD_SESOLA)) { WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaWatchBioCallback() !&X !&X !SL !SL !SL", cmd, argp, argi, argl, retval); switch (cmd & 0xf) { case BIO_CB_READ : WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "!&?after\rbefore\r READ !UL !SL", cmd & BIO_CB_RETURN, argi, retval); break; case BIO_CB_WRITE : WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "!&?after\rbefore\r WRITE !UL !SL", cmd & BIO_CB_RETURN, argi, retval); break; case BIO_CB_FREE : WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "!&?after\rbefore\r FREE !SL !SL", cmd & BIO_CB_RETURN, argi, retval); break; case BIO_CB_CTRL : WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "!&?after\rbefore\r CTRL !SL !SL", cmd & BIO_CB_RETURN, argi, retval); break; default : WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "!&?after\rbefore\r !&X !UL !SL", cmd & BIO_CB_RETURN, cmd, argi, retval); } return (retval); } if (!(cmd & BIO_CB_RETURN)) return (retval); switch (cmd & 0xf) { case BIO_CB_READ : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "READ !UL/!SL!&? (complete)\r (outstanding)\r", argi, retval, retval >= 0); break; case BIO_CB_WRITE : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "WRITE !UL/!SL!&? (complete)\r (outstanding)\r", argi, retval, retval >= 0); break; case BIO_CB_FREE : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "FREE !SL !SL", argi, retval); break; case BIO_CB_CTRL : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "CTRL !SL !SL", argi, retval); break; default : WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "!&X !UL !SL", cmd, argi, retval); } #endif /* WATCH_CAT */ return (retval); } /*****************************************************************************/ /* Provide WATCH information after final SSL session establishment show version and cipher in use. */ SesolaWatchSession (SESOLA_STRUCT *sesolaptr) { #if WATCH_CAT int AlgKeySize, UseKeySize; char *cptr; SSL *SslPtr; SSL_CIPHER *CipherPtr; X509 *CertPtr; /*********/ /* begin */ /*********/ if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaWatchSession()"); SslPtr = sesolaptr->SslPtr; if (CipherPtr = SSL_get_current_cipher (SslPtr)) { cptr = (char*)SSL_CIPHER_get_name (CipherPtr); UseKeySize = SSL_CIPHER_get_bits(CipherPtr, &AlgKeySize); } else { AlgKeySize = UseKeySize = 0; cptr = "(none)"; } WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "SESSION hit:!AZ protocol:!AZ cipher:!AZ keysize:!UL/!UL X509:!AZ", #ifndef __VAX SSL_cache_hit(sesolaptr->SslPtr) ? "yes" : "no", #else "?(VAX)", #endif (char*)SSL_get_version(SslPtr), cptr, UseKeySize, AlgKeySize, (CertPtr = SSL_get_peer_certificate (SslPtr)) ? "yes" : "no"); if (CertPtr) X509_free (CertPtr); #endif /* WATCH_CAT */ } /*****************************************************************************/ /* Provide some OpenSSL-internal error information. (Based on code from [.CRYPTO.ERR]ERR_PRN.C) */ SesolaWatchErrors (SESOLA_STRUCT *sesolaptr) { #if WATCH_CAT int cnt, flags, LineNumber; ulong ErrorNumber; char ErrorString [256], ModuleName [256]; char *cptr, *cptr1, *cptr2, *cptr3, *sptr, *FileNamePtr, *DataPtr; /*********/ /* begin */ /*********/ if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaWatchErrors()"); while (cnt = ERR_get_error_line_data (&FileNamePtr, &LineNumber, &DataPtr, &flags)) { ERR_error_string_n (cnt, ErrorString, sizeof(ErrorString)); for (cptr1 = ErrorString; *cptr1 && *cptr1 != ':'; cptr1++); if (*cptr1) cptr1++; while (*cptr1 && *cptr1 != ':') cptr1++; if (*cptr1) cptr1++; for (cptr2 = cptr1; *cptr2 && *cptr2 != ':'; cptr2++); if (*cptr2) *cptr2++ = '\0'; for (cptr3 = cptr2; *cptr3 && *cptr3 != ':'; cptr3++); if (*cptr3) *cptr3++ = '\0'; for (cptr = FileNamePtr; *cptr; cptr++); while (cptr > FileNamePtr && *cptr != ']') cptr--; if (*cptr == ']') cptr++; sptr = ModuleName; while (*cptr && *cptr != ';') *sptr++ = *cptr++; *sptr = '\0'; WatchThis (WATCHITM(sesolaptr), WATCH_SESOLA, "!AZ !AZ !AZ !AZ!&? \r\r(!AZ:!UL)", cptr1, cptr2, cptr3, flags & ERR_TXT_STRING ? DataPtr : "", flags & ERR_TXT_STRING, ModuleName, LineNumber); } #endif /* WATCH_CAT */ } /*****************************************************************************/ /* Peek at selected SesolaStruct data. The 'rqptr' is the request doing the peeking, the 'rqeptr' is the request being peeked at. If 'rqptr' is NULL then the information is written to SYS$OUTPUT. */ void SesolaWatchPeek ( REQUEST_STRUCT *rqptr, void *SeSoLaPtr ) { static char HexDigits [] = "0123456789abcdef"; static char SesolaFao [] = "|\n\ !33!> !&X\n\ !33 protocol:!AZ cipher:!AZ keysize:!UL/!UL\n\ !33 !AZ\n\ !33 !AZ\n\ !33 !AZ\n\ !33 !&B\n\ !33 !&A\n\ !33Channel!> !UL (!AZ)\n\ !33 !&B\n\ !33 !&B\n\ !33 !&A\n\ !33 !&X\n\ !33 !UL\n\ !33 !&B\n\ !33 !&B\n\ !33 !&X\n\ !33 !UL\n\ !33 !&B\n\ !33 !&B\n\ !33 !UL\n\ !33 !UL\n\ !33 !UL\n"; int cnt, status, AlgKeySize, SessionHint, UseKeySize; ushort Length; ulong *vecptr; ulong FaoVector [48]; char *cptr, *sptr, *zptr, *CipherNamePtr; char Buffer [4096], CertFingerprintMD5 [64], CertFingerprintSHA1 [64], CertFingerprintSHA256 [128], ClientCertBuffer [1024], ConnectCertBuffer [1024], NetDevName [64], IssuerString [256], NotAfterString [32], SessionId [64], SubjectString [256]; SESOLA_STRUCT *sesolaptr; SSL *SslPtr; SSL_CIPHER *CipherPtr; SSL_SESSION *SessionPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaWatchPeek()\n"); if ((sesolaptr = (SESOLA_STRUCT*)SeSoLaPtr) == NULL) return; NetGetBgDevice (sesolaptr->NetIoPtr->Channel, NetDevName, sizeof(NetDevName)); SslPtr = sesolaptr->SslPtr; SessionPtr = SSL_get_session (SslPtr); if (CipherPtr = SSL_get_current_cipher (SslPtr)) { CipherNamePtr = (char*)SSL_CIPHER_get_name (CipherPtr); UseKeySize = SSL_CIPHER_get_bits(CipherPtr, &AlgKeySize); } else AlgKeySize = UseKeySize = 0; if (sesolaptr->ConnectCertPtr) { X509_NAME_oneline (X509_get_issuer_name(sesolaptr->ConnectCertPtr), IssuerString, sizeof(SubjectString)); X509_NAME_oneline (X509_get_subject_name(sesolaptr->ConnectCertPtr), SubjectString, sizeof(SubjectString)); SesolaCertFingerprint (sesolaptr->ConnectCertPtr, &EVP_sha256, CertFingerprintSHA256, sizeof(CertFingerprintSHA256)); SesolaCertFingerprint (sesolaptr->ConnectCertPtr, &EVP_sha1, CertFingerprintSHA1, sizeof(CertFingerprintSHA1)); SesolaCertFingerprint (sesolaptr->ConnectCertPtr, &EVP_md5, CertFingerprintMD5, sizeof(CertFingerprintMD5)); ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(sesolaptr->ConnectCertPtr)); BIO_gets (SesolaBioMemPtr, NotAfterString, sizeof(NotAfterString)); FaoToBuffer (ConnectCertBuffer, sizeof(ConnectCertBuffer), NULL, "issuer=!AZ\n\ !33 subject=!AZ\n\ !33 notAfter=!AZ\n\ !33 SHA256=!AZ\n\ !33 SHA1=!AZ\n\ !33 MD5=!AZ", IssuerString, SubjectString, NotAfterString, CertFingerprintSHA256, CertFingerprintSHA1, CertFingerprintMD5); } else strcpy (ConnectCertBuffer, "0x00000000"); if (sesolaptr->ClientCertPtr) { X509_NAME_oneline (X509_get_issuer_name(sesolaptr->ClientCertPtr), IssuerString, sizeof(SubjectString)); X509_NAME_oneline (X509_get_subject_name(sesolaptr->ClientCertPtr), SubjectString, sizeof(SubjectString)); SesolaCertFingerprint (sesolaptr->ClientCertPtr, &EVP_sha256, CertFingerprintSHA256, sizeof(CertFingerprintSHA256)); SesolaCertFingerprint (sesolaptr->ClientCertPtr, &EVP_sha1, CertFingerprintSHA1, sizeof(CertFingerprintSHA1)); SesolaCertFingerprint (sesolaptr->ClientCertPtr, &EVP_md5, CertFingerprintMD5, sizeof(CertFingerprintMD5)); ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(sesolaptr->ClientCertPtr)); BIO_gets (SesolaBioMemPtr, NotAfterString, sizeof(NotAfterString)); FaoToBuffer (ClientCertBuffer, sizeof(ClientCertBuffer), NULL, "issuer=!AZ\n\ !33 subject=!AZ\n\ !33 notAfter=!AZ\n\ !33 SHA256=!AZ\n\ !33 SHA1=!AZ\n\ !33 MD5=!AZ", IssuerString, SubjectString, NotAfterString, CertFingerprintSHA256, CertFingerprintSHA1, CertFingerprintMD5); } else strcpy (ClientCertBuffer, "0x00000000"); cptr = SSL_SESSION_get_id (SessionPtr, &cnt); zptr = (sptr = SessionId) + sizeof(SessionId)-1; if (cnt) { while (cnt--) { if (sptr >= zptr) break; *sptr++ = HexDigits[(*cptr >> 4) & 0x0f]; if (sptr >= zptr) break; *sptr++ = HexDigits[*cptr++ & 0x0f]; } } else for (cptr = "(none)"; *cptr; *sptr++ = *cptr++); *sptr = '\0'; vecptr = FaoVector; *vecptr++ = sesolaptr; *vecptr++ = SSL_get_version(SslPtr); *vecptr++ = CipherNamePtr; *vecptr++ = UseKeySize; *vecptr++ = AlgKeySize; *vecptr++ = SessionId; *vecptr++ = ConnectCertBuffer; *vecptr++ = ClientCertBuffer; *vecptr++ = sesolaptr->CertVerifyFailed; *vecptr++ = sesolaptr->ClientCertAstFunction; *vecptr++ = sesolaptr->NetIoPtr->Channel; *vecptr++ = NetDevName+1; *vecptr++ = sesolaptr->ReadInProgress; *vecptr++ = sesolaptr->ReadIsComplete; *vecptr++ = sesolaptr->SslStateFunction; *vecptr++ = sesolaptr->ReadPtr; *vecptr++ = sesolaptr->ReadSize; *vecptr++ = sesolaptr->WriteInProgress; *vecptr++ = sesolaptr->WriteIsComplete; *vecptr++ = sesolaptr->WritePtr; *vecptr++ = sesolaptr->WriteLength; *vecptr++ = sesolaptr->HTTPduringHandshake; *vecptr++ = sesolaptr->SSHduringHandshake; *vecptr++ = sesolaptr->SslAcceptCount; *vecptr++ = sesolaptr->SslConnectCount; *vecptr++ = sesolaptr->SslShutdownCount; status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, SesolaFao, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); if (rqptr) WatchWrite (Buffer, Length); else fputs (Buffer, stdout); } /*****************************************************************************/ /* Brief report on this or a virtual service SSL context statistics and any current session information. */ SesolaReport ( REQUEST_STRUCT *rqptr, char *VirtualHostPort ) { static char VirtualFao [] = "\n\
\n\

\n\ \n\ \n\
Virtual SSL Server
\n\ \n\ \n\ \n\
\n\

\n\

\n\ \n\ \n\
!AZ
\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \ \n\ \ \n\ \n\ \n"; static char EndServerInfoFao [] = "
Version:!AZ (0x!8XL)
Build:!AZ
Protocol:!AZ
Options:\ 0x!8XL; !AZ
DH param:!AZ
Server Certificate:!AZ
\n\ \n\ \ \n\ \ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\
Issuer:!AZ
Subject:!AZ
Extensions:\n"; static char ExtensionFao [] = "\ \n"; static char ExtensionNone [] = "\n"; static char ServerInfo2Fao [] = "
!UL.!AZ
(none)
\n\
Begins:!AZ
Expires:!AZ
SHA256:!AZ
SHA1:!AZ
MD5:!AZ
\n\
Private Key:!AZ
CA Verify File:!&@
CA Verify Depth:!&@
Cipher List:!AZ!AZ
Supported Ciphers:\n\ \n\ \ \n"; static char CiphersFao [] = "\ \ \ \n"; static char CiphersNoneFao [] = "\ \n"; static char EndCiphersFao [] = "
VersionName
!UL.!AZ!AZ
(n/a)
\n\
TLS/SSL:\ \n\ \n\ \n" #ifndef __VAX #if SESOLA_SINCE_111 "\n" #endif "\n\ \n" #endif "\n" #if SESOLA_BEFORE_111 "\n" #endif #ifdef __VAX "\n" #endif "
Connect:!UL(!UL%)
Request:!UL(!UL%)
TLSv1.3:!UL(!UL%)
TLSv1.2:!UL(!UL%)
TLSv1.1:!UL(!UL%)
TLSv1:!UL(!UL%)
SSLv3:!UL(!UL%)
SSLv2:!UL(!UL%)
\n\
Session Ticket:\n\ \n\ !&@\
\n\
Session ID Cache:\n\ \n\ !&@\
\n\
\n\
\n"; static char CurrentSessionFao [] = "

\n\ \n\ \n"; static char EndPageFao [] = "
Current Session
\n\ \n\ \ \n"; static char ClientCert1Fao [] = "\n"; static char ClientCertNoneFao [] = "\ \n"; static char CurrentSessionEndFao [] = "
Session:!AZ (!2ZL:!2ZL:!2ZL ago), timeout (!2ZL:!2ZL:!2ZL) at !AZ
Client Certificate:\n\ \n\ \ \n\ \ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\
Issuer:!AZ
Subject:!AZ
Extensions:\n"; static char ClientCert2Fao [] = "
\n\
Begins:!AZ
Expires:!AZ
SHA256:!AZ
SHA1:!AZ
MD5:!AZ
Fingerprint:!AZ
\n\
Client Certificate:!AZ \"!AZ\">Get Client Certificate!AZ
\n\
\n\ \n\ \n\ \n"; int options, status, AlgKeySize, Count, SessionHits, SessionTimeout, SessionTimeCSec, SessionTimeoutCSec, ServiceCount, VersionTotal, Total, UseKeySize, UtcOffset; char *cptr, *sptr, *zptr; ulong BinaryTime [2], /* WARNING: really does need to be fairly large!! */ FaoVector [256], ResultTime [2]; ulong *vecptr; char AuthFingerprint [64], CertString [256], CertCaString [256], CertDnString [256], CertFingerprintMD5 [64], CertFingerprintSHA1 [64], CertFingerprintSHA256 [128], CertNotAfterString [32], CertNotBeforeString [32], ScratchString [256], TimeString [32], TimeoutString [32]; SESOLA_STRUCT *sesolaptr; SERVICE_STRUCT *svptr, *tsvptr; struct tm *tmptr; SESOLA_CONTEXT *scptr; SSL *SslPtr; SSL_CTX *SslCtx; SSL_CIPHER *CipherPtr; SSL_SESSION *SessionPtr; STACK_OF(SSL_CIPHER) *CipherStackPtr; X509 *ServerCertPtr; /*********/ /* begin */ /*********/ /* as this is also called as an AST (!!) avoid WATCHing parameters */ if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaReport()"); sesolaptr = NULL; if (HTTP2_REQUEST(rqptr)) sesolaptr = (SESOLA_STRUCT*)rqptr->Http2Stream.Http2Ptr->NetIoPtr->SesolaPtr; if (!sesolaptr) sesolaptr = (SESOLA_STRUCT*)rqptr->NetIoPtr->SesolaPtr; if (sesolaptr) { if (sesolaptr->ReportVirtualHostPort) { /* unbuffer this if called from an SSL renegotiate AST (see below) */ VirtualHostPort = sesolaptr->ReportVirtualHostPort; } } /* now (!!) we can report "parameters" */ if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "!&Z", VirtualHostPort); if (VirtualHostPort[0]) { LIST_ITERATE (svptr, &ServiceList) { if (svptr->SchemeType != SCHEME_HTTPS) continue; if (strsame (svptr->ServerHostPort, VirtualHostPort, -1)) break; } if (!svptr) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, "No such virtual service.", FI_LI); AdminEnd (rqptr); return; } } else svptr = rqptr->ServicePtr; /* if request's service not SSL then choose the first in the list */ if (svptr->SchemeType != SCHEME_HTTPS) { LIST_ITERATE (svptr, &ServiceList) if (svptr->SchemeType != SCHEME_HTTPS) continue; if (svptr) VirtualHostPort = svptr->ServerHostPort; } if (svptr->SchemeType != SCHEME_HTTPS) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, "No SSL service found!", FI_LI); AdminEnd (rqptr); return; } ServiceCount = SesolaServiceCount(); if (sesolaptr) { if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SSL_CLIENT, -1)) { /* Include any client certificate information. Note that if SesolaClientCert() completes asynchronously this function will be called again as an AST - with only one argument!! (the 'rqptr') This is not a major issue (apart for the purists ;^) VirtualHostPort is buffered and unbuffered around such a call here. (I could have made it a variable argument function, but this should work just as well!) */ if (sesolaptr->ReportClientCertState == 0) { sesolaptr->ReportClientCertState = 1; zptr = (sptr = sesolaptr->ReportVirtualHostPort) + sizeof(sesolaptr->ReportVirtualHostPort)-1; for (cptr = VirtualHostPort; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; /* using renegotiation, cancel any existing certificate */ status = SesolaClientCert (rqptr, SESOLA_VERIFY_PEER_NONE, &SesolaReport); /* this faux status indicates renegotiation is underway */ if (status == SS$_WAITUSRLBL) return; /* any other error just aborts the client certificate processing */ if (VMSnok (status)) sesolaptr->ReportClientCertState = 2; } if (sesolaptr->ReportClientCertState == 1) { sesolaptr->ReportClientCertState = 2; /* using (more) renegotiation, get a(nother) certificate */ status = SesolaClientCert (rqptr, SESOLA_VERIFY_PEER_OPTIONAL, &SesolaReport); /* this faux status indicates renegotiation is underway */ if (status == SS$_WAITUSRLBL) return; } } } /**********/ /* report */ /**********/ /* the sesola service context */ scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr; /* this is the service's context - not the current session's! */ SslCtx = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx; /* get the service's certificate and ciphers - not the current session's! */ SslPtr = SSL_new (SslCtx); if (!SslPtr) { ErrorNoticed (rqptr, 0, "SSL_new() failed", FI_LI); ErrorGeneral (rqptr, "SSL_new() failed.", FI_LI); AdminEnd (rqptr); return; } ServerCertPtr = SSL_get_certificate (SslPtr); CipherStackPtr = SSL_get_ciphers (SslPtr); #ifndef SESOLA_REPORT_DN SesolaCertReportName (ServerCertPtr, "ISSUER", CertCaString, sizeof(CertCaString)); SesolaCertReportName (ServerCertPtr, "SUBJECT", CertDnString, sizeof(CertDnString)); #else X509_NAME_oneline (X509_get_issuer_name(ServerCertPtr), CertString, sizeof(CertString)); SesolaCertReportDn (CertString, CertCaString, sizeof(CertCaString)); X509_NAME_oneline (X509_get_subject_name(ServerCertPtr), CertString, sizeof(CertString)); SesolaCertReportDn (CertString, CertDnString, sizeof(CertDnString)); #endif ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notBefore(ServerCertPtr)); BIO_gets (SesolaBioMemPtr, CertNotBeforeString, sizeof(CertNotBeforeString)); ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(ServerCertPtr)); BIO_gets (SesolaBioMemPtr, CertNotAfterString, sizeof(CertNotAfterString)); SesolaCertFingerprint (ServerCertPtr, &EVP_sha256, CertFingerprintSHA256, sizeof(CertFingerprintSHA256)); SesolaCertFingerprint (ServerCertPtr, &EVP_sha1, CertFingerprintSHA1, sizeof(CertFingerprintSHA1)); SesolaCertFingerprint (ServerCertPtr, &EVP_md5, CertFingerprintMD5, sizeof(CertFingerprintMD5)); FaoToBuffer (ScratchString, sizeof(ScratchString), NULL, "SSL Report  –  !AZ", svptr->ServerHostPort); AdminPageTitle (rqptr, ScratchString); vecptr = FaoVector; *vecptr++ = ADMIN_REPORT_SSL; *vecptr++ = VirtualHostPort[0]; *vecptr++ = (ServiceCount == 1); status = FaolToNet (rqptr, VirtualFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (ServiceCount > 1) { LIST_ITERATE (tsvptr, &ServiceList) { if (tsvptr->SchemeType != SCHEME_HTTPS) continue; vecptr = FaoVector; *vecptr++ = tsvptr->ServerHostPort; *vecptr++ = strsame (tsvptr->ServerHostPort, VirtualHostPort, -1); *vecptr++ = tsvptr->ServerHostPort; status = FaolToNet (rqptr, "

\n\ \n\ \n\
!AZ
\n\ \n\ \n\ \n"; static char CertStoreNull [] = "\n"; static char CertFao [] = "\n\ \n"; static char EndPageFao [] = "\n\ \n\
CA Verify File:!AZ
CA Verify Depth:!UL
(none)
\
\n\ \n\ \ \ \n\ \ \n\ \n\ \n\
!UL.Issuer:!AZ
Subject:!AZ
Begins:!AZ
Expires:!AZ
\n\
\
\n\ Edit\n\ List\n\ Reload\n\
\n\
\n\ \n\ \n\ \n"; int idx, status, Count, ObjectCount; ulong FaoVector [16]; ulong *vecptr; char *cptr, *sptr, *zptr; char CertString [256], CertCaString [256], CertDnString [256], CertNotAfterString [32], CertNotBeforeString [32]; SERVICE_STRUCT *svptr; SESOLA_CONTEXT *scptr; SSL_CTX *SslCtx; X509 *CertPtr; X509_OBJECT *CertObjectPtr; X509_STORE *CertStorePtr; #if SESOLA_SINCE_110 STACK_OF(X509_OBJECT) *ObjectsPtr; #endif /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaReportCA() !AZ", VirtualHostPort); if (VirtualHostPort[0]) { LIST_ITERATE (svptr, &ServiceList) if (strsame (svptr->ServerHostPort, VirtualHostPort, -1)) break; if (!svptr) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, "No such virtual service.", FI_LI); AdminEnd (rqptr); return; } } else svptr = rqptr->ServicePtr; /* if the service is not SSL then choose the first in the list */ if (svptr->SchemeType != SCHEME_HTTPS) { LIST_ITERATE (svptr, &ServiceList) if (svptr->SchemeType != SCHEME_HTTPS) continue; if (svptr) VirtualHostPort = svptr->ServerHostPort; } if (svptr->SchemeType != SCHEME_HTTPS) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, "No SSL service found!", FI_LI); AdminEnd (rqptr); return; } /* the sesola service context */ scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr; /* this is the service's context - not the current session's! */ SslCtx = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx; AdminPageTitle (rqptr, "SSL Report, CA Verification"); vecptr = FaoVector; *vecptr++ = svptr->ServerHostPort; *vecptr++ = scptr->CaFilePtr; *vecptr++ = SSL_CTX_get_verify_depth (SslCtx); status = FaolToNet (rqptr, BeginPageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (!(CertStorePtr = SSL_CTX_get_cert_store (SslCtx))) { status = FaolToNet (rqptr, CertStoreNull, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } else { Count = 0; #if SESOLA_SINCE_110 ObjectsPtr = X509_STORE_get0_objects(CertStorePtr); ObjectCount = sk_X509_OBJECT_num(ObjectsPtr); for (idx = 0; idx < ObjectCount; idx++) { CertObjectPtr = sk_X509_OBJECT_value (ObjectsPtr, idx); if (X509_OBJECT_get_type(CertObjectPtr) != X509_LU_X509) continue; CertPtr = X509_OBJECT_get0_X509 (CertObjectPtr); #else /* SESOLA_SINCE_110 */ ObjectCount = sk_X509_OBJECT_num(CertStorePtr->objs); for (idx = 0; idx < ObjectCount; idx++) { CertObjectPtr = sk_X509_OBJECT_value (CertStorePtr->objs, idx); if (CertObjectPtr->type != X509_LU_X509) continue; CertPtr = (X509*)CertObjectPtr->data.x509; #endif /* SESOLA_SINCE_110 */ Count++; #ifndef SESOLA_REPORT_DN SesolaCertReportName (CertPtr, "ISSUER", CertCaString, sizeof(CertCaString)); SesolaCertReportName (CertPtr, "SUBJECT", CertDnString, sizeof(CertDnString)); #else X509_NAME_oneline (X509_get_issuer_name(CertPtr), CertString, sizeof(CertString)); SesolaCertReportDn (CertString, CertCaString, sizeof(CertCaString)); X509_NAME_oneline (X509_get_subject_name(CertPtr), CertString, sizeof(CertString)); SesolaCertReportDn (CertString, CertDnString, sizeof(CertDnString)); #endif ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notBefore(CertPtr)); BIO_gets (SesolaBioMemPtr, CertNotBeforeString, sizeof(CertNotBeforeString)); ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(CertPtr)); BIO_gets (SesolaBioMemPtr, CertNotAfterString, sizeof(CertNotAfterString)); vecptr = FaoVector; *vecptr++ = Count; *vecptr++ = CertCaString; *vecptr++ = CertDnString; *vecptr++ = CertNotBeforeString; *vecptr++ = CertNotAfterString; status = FaolToNet (rqptr, CertFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } vecptr = FaoVector; *vecptr++ = ADMIN_REVISE_SSL_CA; *vecptr++ = svptr->ServerHostPort; *vecptr++ = ADMIN_REPORT_SSL_CA; *vecptr++ = svptr->ServerHostPort; *vecptr++ = ADMIN_CONTROL_SSL_CA_LOAD; status = FaolToNet (rqptr, EndPageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); AdminEnd (rqptr); } /*****************************************************************************/ /* Command-line CA verification file reload function. */ void SesolaControlReloadCA () { int cnt, value, ErrorCount, SSLcount, ReloadCount; SERVICE_STRUCT *svptr; SESOLA_CONTEXT *scptr; SSL_CTX *SslCtx; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaControlReloadCA()"); /* enable SYSPRV to allow access to a possibly protected file */ sys$setprv (1, &SysPrvMask, 0, 0); ErrorCount = SSLcount = ReloadCount = 0; LIST_ITERATE (svptr, &ServiceList) { if (svptr->SchemeType != SCHEME_HTTPS) continue; for (cnt = 0; cnt < 2; cnt++) { if (cnt) scptr = (SESOLA_CONTEXT*)svptr->SSLclientPtr; else scptr = (SESOLA_CONTEXT*)svptr->SSLserverPtr; if (!scptr) continue; if (!scptr->SslCtx) continue; SSLcount++; if (!scptr->CaFilePtr[0]) continue; SslCtx = (SSL_CTX*)scptr->SslCtx; SSL_CTX_set_cert_store (SslCtx, X509_STORE_new()); /* load the file in afresh */ value = SSL_CTX_load_verify_locations (SslCtx, scptr->CaFilePtr, NULL); if (value) value = SSL_CTX_set_default_verify_paths (SslCtx); if (value) ReloadCount++; else { ErrorCount++; FaoToStdout ("%HTTPD-W-SSL, !AZ\n \\!AZ\\\n", svptr->ServerHostPort, scptr->CaFilePtr); SesolaPrintOpenSslErrorList (); FaoToStdout ("%HTTPD-W-SSL, client verification not enabled\n"); } } } sys$setprv (0, &SysPrvMask, 0, 0); FaoToStdout ("%HTTPD-I-SSL, \ reloaded CAs for !UL of !UL SSL contexts!&@\n", ReloadCount, SSLcount, ErrorCount ? ", !UL error!%s" : "", ErrorCount); if (OpcomMessages & OPCOM_HTTPD || OpcomMessages & OPCOM_AUTHORIZATION) FaoToOpcom ("%HTTPD-I-SSL, \ reloaded CAs for !UL of !UL SSL contexts!&@", ReloadCount, SSLcount, ErrorCount ? ", !UL error!%s" : "", ErrorCount); } /*****************************************************************************/ /* */ char* SesolaRequestCipher (SESOLA_STRUCT *sesolaptr) { char *cptr; SSL_CIPHER *CipherPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestCipher()"); if (CipherPtr = SSL_get_current_cipher (sesolaptr->SslPtr)) cptr = (char*)SSL_CIPHER_get_name (CipherPtr); else cptr = "*BUGCHECK*"; return (cptr); } /*****************************************************************************/ /* */ BOOL SesolaRequestSessionReused (SESOLA_STRUCT *sesolaptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestSessionReused()"); #ifndef __VAX return ((BOOL)SSL_cache_hit(sesolaptr->SslPtr)); #else return (false); #endif } /*****************************************************************************/ /* */ char* SesolaRequestVersion (SESOLA_STRUCT *sesolaptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestVersion()"); return ((char*)SSL_get_version(sesolaptr->SslPtr)); } /*****************************************************************************/ /* Return a pointer to a static string containing the string equivalent of the bit vector that is OpenSSL options. Parameter is byt reference. */ char* SesolaOptionsAsString (ulong *optptr) { static char buf [1024]; ulong bit, options; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaOptionsAsString()"); options = *optptr; zptr = (sptr = buf) + sizeof(buf)-4; for (bit = 0x80000000; bit; bit = bit >> 1) { switch (bit & options) { case 0 : cptr = NULL; break; case 0x00000001 : cptr = "microsoft_sess_id_bug"; break; case 0x00000002 : cptr = "netscape_challenge_bug"; break; case 0x00000004 : cptr = "legacy_server_connect"; break; case 0x00000008 : cptr = "netscape_reuse_cipher_change_bug"; break; case 0x00000010 : cptr = "tlsext_padding"; break; case 0x00000020 : cptr = "microsoft_big_sslv3_buffer"; break; case 0x00000040 : cptr = "safari_ecdhe_ecdsa_bug"; break; case 0x00000080 : cptr = "ssleay_080_client_dh_bug"; break; case 0x00000100 : cptr = "tls_d5_bug"; break; case 0x00000200 : cptr = "tls_block_padding_bug"; break; case 0x00000400 : cptr = "msie_sslv2_rsa_padding"; break; case 0x00000800 : cptr = "sslref2_reuse_cert_type_bug"; break; case 0x00001000 : cptr = "no_query_mtu"; break; case 0x00002000 : cptr = "cookie_exchange"; break; case 0x00004000 : cptr = "no_ticket"; break; case 0x00008000 : cptr = "cisco_anyconnect"; break; case 0x00010000 : cptr = "no_session_resumption_on_renegotiation"; break; case 0x00020000 : cptr = "no_compression"; break; case 0x00040000 : cptr = "allow_unsafe_legacy_renegotiation"; break; case 0x00080000 : cptr = "single_ecdh_use"; break; case 0x00100000 : cptr = "single_dh_use"; break; case 0x00200000 : cptr = "ephemeral_rsa"; break; case 0x00400000 : cptr = "cipher_server_preference"; break; case 0x00800000 : cptr = "tls_rollback_bug"; break; case 0x01000000 : cptr = "no_sslv2"; break; case 0x02000000 : cptr = "no_sslv3"; break; case 0x04000000 : cptr = "no_tlsv1"; break; case 0x08000000 : cptr = "no_tlsv1_2"; break; case 0x10000000 : cptr = "no_tlsv1_1"; break; case 0x20000000 : cptr = "netscape_ca_dn_bug"; break; case 0x40000000 : cptr = "netscape_demo_cipher_change_bug"; break; case 0x80000000 : cptr = "cryptopro_tlsext_bug"; break; default : cptr = "BUGCHECK"; } if (cptr) { if (sptr > buf) { if (sptr < zptr) *sptr++ = ','; if (sptr < zptr) *sptr++ = ' '; } while (*cptr && sptr < zptr) *sptr++ = *cptr++; } } if (sptr >= zptr) for (cptr = "..."; *cptr; *sptr++ = *cptr++); *sptr = '\0'; return (buf); } /*****************************************************************************/ /* This is basically for inclusion in the WATCH report header. */ char* SesolaVersion (BOOL full) { static char String [256]; static $DESCRIPTOR (StringDsc, String); static $DESCRIPTOR (FullVersionFaoDsc, "!AZ (!AZ) !AZ*.H !AZ\n\0"); static $DESCRIPTOR (VersionFaoDsc, "!AZ (!AZ)\n\0"); char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaVersion()"); cptr = (char*)OpenSSL_version (OPENSSL_BUILT_ON); if (sptr = strstr (cptr, "uilt on")) { cptr = sptr + 7; while (*cptr && (*cptr == ' ' || *cptr == ':')) cptr++; } /* this globavalue is set by the linker when building the image */ switch (SESOLA_LIB_SSL_32) { case 11 : sptr = "SSLLIB:LIBSSL.OLB"; break; case 12 : sptr = "SSLLIB:SSL_LIBSSL32.OLB"; break; case 21 : sptr = "SYS$COMMON:[SYSLIB]SSL$LIBSSL_SHR32.EXE"; break; case 22 : sptr = "SYS$COMMON:[SYSLIB]SSL1$LIBSSL_SHR32.EXE"; break; case 23 : sptr = "SYS$COMMON:[SYSLIB]SSL111$LIBSSL_SHR32.EXE"; break; case 31 : sptr = "OPENSSL097E_LIBSSL_SHR32.EXE"; break; case 41 : sptr = "WASD [ALPHA.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 42 : sptr = "WASD [ALPHA.EXE.SSL]LIBSSL.OLB"; break; case 51 : sptr = "WASD [AXP.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 52 : sptr = "WASD [IA64.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 53 : sptr = "WASD [VAX.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 54 : sptr = "WASD [AXP.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 55 : sptr = "WASD [IA64.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 56 : sptr = "WASD [VAX.EXE.SSL]SSL_LIBSSL32.OLB"; break; case 61 : sptr = "OSSL$LIB:OSSL$LIBCRYPTO32.OLB"; break; case 62 : sptr = "OSSL$SHARE:OSSL$LIBSSL0101_SHR32.EXE"; break; case 71 : sptr = "WASD [WASD.ALPHA]LIBSSL32.OLB"; break; case 72 : sptr = "WASD [WASD.IA64]LIBSSL32.OLB"; break; default : sptr = "?"; break; } if (full) sys$fao (&FullVersionFaoDsc, 0, &StringDsc, OpenSSL_version (OPENSSL_VERSION), cptr, LIB_SSL_32, sptr); else sys$fao (&VersionFaoDsc, 0, &StringDsc, OpenSSL_version (OPENSSL_VERSION), cptr); return (String); } /*****************************************************************************/ /* Fudge what's not in these versions. */ #if SESOLA_BEFORE_110 unsigned long OpenSSL_version_num(void) { int fix = 0, maj = 0, min = 0; ulong vnum = 0; char pch = 0; char *cptr; /* ulong with MNNFFPPS: major minor fix patch status */ for (cptr = OpenSSL_version(OPENSSL_VERSION); *cptr && !isdigit(*cptr); cptr++); sscanf (cptr, "%d.%d.%d%c", &maj, &min, &fix, &pch); if (!isalpha(pch)) pch = 0; if (pch >= 'a' && pch <= 'z') pch -= 96; vnum = maj * 0x10000000L + min * 0x00100000L + fix * 0x00001000L + pch * 0x00000010L; return (vnum); } const char* OpenSSL_version (int t) { return (SSLeay_version(t)); } #endif /* SESOLA_BEFORE_110 */ /*****************************************************************************/ /* Wrapper for keeping track of OpenSSL malloc()s for debug/development. */ #ifndef __VAX void* SesolaMalloc (int size) { void *mptr; /*********/ /* begin */ /*********/ if (SesolaMemVmTrack) mptr = VmOpenSslMalloc (size + sizeof(ulong)*2); else mptr = malloc (size + sizeof(ulong)*2); if (!mptr) return (mptr); *(ulong*)mptr = size; (uchar*)mptr += sizeof(ulong); *(ulong*)mptr = 0xfee1dead; (uchar*)mptr += sizeof(ulong); SesolaMallocCount++; SesolaMallocBytes += (ulong)size; SesolaInUseBytes = SesolaMallocBytes - SesolaFreeBytes; if (SesolaInUseBytes > SesolaMaxBytes) SesolaMaxBytes = SesolaInUseBytes; return (mptr); } #endif /* VAX */ /*****************************************************************************/ /* Wrapper for keeping track of OpenSSL realloc()s for debug/development. */ #ifndef __VAX void* SesolaRealloc (void *mptr, int size) { ulong msize; /*********/ /* begin */ /*********/ if (!mptr) return (SesolaMalloc (size)); (uchar*)mptr -= sizeof(ulong); if (*(ulong*)mptr != 0xfee1dead) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); (uchar*)mptr -= sizeof(ulong); msize = *(ulong*)mptr; if (size <= msize) { (uchar*)mptr += sizeof(ulong)*2; return (mptr); } if (SesolaMemVmTrack) mptr = VmOpenSslRealloc (mptr, size + sizeof(ulong)*2); else mptr = realloc (mptr, size + sizeof(ulong)*2); if (!mptr) return (mptr); (uchar*)mptr += sizeof(ulong)*2; SesolaReallocCount++; /* with VmOpenSslRealloc() this is done as an alloc and free */ SesolaMallocBytes += (ulong)size; SesolaFreeBytes += msize; SesolaInUseBytes = SesolaMallocBytes - SesolaFreeBytes; if (SesolaInUseBytes > SesolaMaxBytes) SesolaMaxBytes = SesolaInUseBytes; return (mptr); } #endif /* VAX */ /*****************************************************************************/ /* Wrapper for keeping track of OpenSSL free()s for debug/development. */ #ifndef __VAX void SesolaFree (void *mptr) { ulong msize; /*********/ /* begin */ /*********/ if (!mptr) return; (uchar*)mptr -= sizeof(ulong); if (*(ulong*)mptr != 0xfee1dead) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); (uchar*)mptr -= sizeof(ulong); msize = *(ulong*)mptr; if (SesolaMemVmTrack) VmOpenSslFree (mptr); else free (mptr); SesolaFreeCount++; SesolaFreeBytes += (ulong)msize; SesolaInUseBytes = SesolaMallocBytes - SesolaFreeBytes; if (SesolaInUseBytes > SesolaMaxBytes) SesolaMaxBytes = SesolaInUseBytes; } #endif /* VAX */ /*****************************************************************************/ /* Return a pointer to a buffer containing the memory track report. */ #ifndef __VAX char* SesolaMemTrackReport (void) { static ulong LastLookTime64; static char buf [192]; int status; __int64 avail64, davail64, dinuse64, dmax64, max64; char LastLookAgo [64]; /*********/ /* begin */ /*********/ if (!SesolaMemTrack) return (NULL); ThisLongAgo (&LastLookTime64, LastLookAgo); dinuse64 = SesolaInUseBytes - SesolaPrevInUseBytes; dmax64 = SesolaMaxBytes - SesolaPrevMaxBytes; avail64 = SesolaMaxBytes - SesolaInUseBytes; davail64 = avail64 - SesolaPrevAvailBytes; status = FaoToBuffer (buf, sizeof(buf), NULL, "!AZ malloc:!&,UL (!&,UL) realloc:!&,UL (!&,UL) free:!&,UL (!&,UL) \ inuse:!&,@SQ (!AZ!&,@SQ) max:!&,@SQ (!AZ!&,@SQ) avail:!&,@SQ (!AZ!&,@SQ) (!AZ)", LastLookAgo, SesolaMallocCount, SesolaMallocCount - SesolaPrevMallocCount, SesolaReallocCount, SesolaReallocCount - SesolaPrevReallocCount, SesolaFreeCount, SesolaFreeCount - SesolaPrevFreeCount, &SesolaInUseBytes, dinuse64 > 0 ? "+" : "", &dinuse64, &SesolaMaxBytes, dmax64 > 0 ? "+" : "", &dmax64, &avail64, davail64 > 0 ? "+" : "", &davail64, SesolaMemVmTrack ? "VM" : "DECC"); if (VMSnok (status)) ErrorNoticed (NULL, status, NULL, FI_LI); SesolaPrevFreeCount = SesolaFreeCount; SesolaPrevMallocCount = SesolaMallocCount; SesolaPrevReallocCount = SesolaReallocCount; SesolaPrevInUseBytes = SesolaInUseBytes; SesolaPrevMaxBytes = SesolaMaxBytes; SesolaPrevAvailBytes = avail64; sys$gettim (&LastLookTime64); return (buf); } #endif /* VAX */ /*****************************************************************************/ /* Reset the counters. */ #ifndef __VAX void SesolaMemTrackReset (void) { /*********/ /* begin */ /*********/ SesolaFreeCount = SesolaMallocCount = SesolaReallocCount = SesolaPrevFreeCount = SesolaPrevMallocCount = SesolaPrevReallocCount = 0; SesolaFreeBytes = SesolaInUseBytes = SesolaMallocBytes = SesolaMaxBytes = SesolaPrevAvailBytes = SesolaPrevFreeBytes = SesolaPrevInUseBytes = SesolaPrevMallocBytes = SesolaPrevMaxBytes = 0; } #endif /* VAX */ /*****************************************************************************/ /* For compilations without SSL these functions provide LINKage stubs for the rest of the HTTPd modules, allowing for just recompiling the Sesola module to integrate the SSL functionality. */ /*********************/ #else /* not SESOLA */ /*********************/ /* required global storage */ BOOL ProtocolHttpsAvailable = false, ProtocolHttpsConfigured = false, SesolaVerifyCAConfigured; /* i.e. disabled */ int SesolaGblSecStructSize = 0, SesolaTicketKeySuperDay, SesolaVersionBitmap = -1; char HttpdSesola [] = "", SesolaAvailableVersions [] = "", SesolaParams [256] = "", SesolaTicketKey [] = ""; /* external storage */ extern char ErrorSanityCheck[]; extern WATCH_STRUCT Watch; SesolaInit () { if (SesolaVersionBitmap <= 0) return; FaoToStdout ("%HTTPD-E-SSL, non-SSL version\n"); exit (STS$K_ERROR | STS$M_INHIB_MSG); } SesolaInitService (SERVICE_STRUCT *svptr) { return; } BOOL SesolaInitClientService (SERVICE_STRUCT *svptr) { return (true); } BOOL SesolaSNIserviceSet (void *sesolaptr) { return (false); } SesolaClientCert ( REQUEST_STRUCT *rqptr, int VerifyMode, REQUEST_AST AstFunction ) { return (AUTH_DENIED_BY_OTHER); } SesolaReport ( REQUEST_STRUCT *rqptr, char *VirtualHostPort ) { if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaReport()"); rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, "This is a non-SSL version.", FI_LI); AdminEnd (rqptr); } void SesolaAdminReloadCA (REQUEST_STRUCT *rqptr) { if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaAdminReloadCA()"); rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, "This is a non-SSL version.", FI_LI); AdminEnd (rqptr); } void SesolaControlReloadCA () { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaControlReloadCA()"); } void SesolaControlReloadCerts () { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaControlReloadCerts()"); } SesolaReportCA ( REQUEST_STRUCT *rqptr, char *VirtualHostPort ) { if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaReportCA()"); rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, "This is a non-SSL version.", FI_LI); AdminEnd (rqptr); } char* SesolaRequestCipher (void *sesolaptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestCipher()"); return ("*BUGCHECK*"); } BOOL SesolaRequestSessionReused (void *sesolaptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestSessionReused()"); return (false); } char* SesolaRequestVersion (void *sesolaptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestVersion()"); return ("*BUGCHECK*"); } char* SesolaSessionTicketNewKey () { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSessionTicketNewKey()"); return; } char* SesolaSessionTicketUseKey (uchar* keyptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSessionTicketUseKey()"); return; } char* SesolaRequestALPN (REQUEST_STRUCT *rqptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestALPN()"); return (NULL); } void* SesolaRequestSesolaPtr (REQUEST_STRUCT *rqptr) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaRequestSesolaPtr()"); return (NULL); } void SesolaSetWatch ( void *sesolaptr, int item ) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaSetWatch()"); } char* SesolaVersion (BOOL full) { if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaVersion()"); return (""); } SesolaWatchPeek ( REQUEST_STRUCT *rqptr, void *SeSoLaPtr ) { if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaWatchPeek()"); return; } /*****************************************************************************/ /************************/ #endif /* ifdef SESOLA */ /************************/ /*****************************************************************************/