/*****************************************************************************/ /* SesolaNet.c SSL network connection establishment, read/write and other functions. The SesolaNetRequest..() functions deal with SSL acceptance and SSL rundown of requests to SSL services (i.e. via "https:"). The SesolaNetClient..() functions deal with initiation and rundown of an SSL transaction with a remote SSL server. This is sometimes refered to as HTTP to SSL gatewaying, and should not be confised with the processing of peer certificate which is also refered to as client processing sometimes. VERSION HISTORY --------------- 16-MAR-2017 MGD further refinements supporting OpenSSL v1.1.0 and TLS 1.3 30-DEC-2016 MGD SesolaNetThisIsSSL() allow redirection to include scheme 03-AUG-2016 MGD OpenSSL v1.1.0(-pre6) required code changes including #if (OPENSSL_VERSION_NUMBER < 0x10100000L) compilation 11-AUG-2015 MGD restructure of network I/O abstractions SesolaNetThisIsSSL() simplify by removing request URI 13-JUN-2015 MGD disable kludge; SesolaNetAccept() SSL3_ST_SR_CLNT_HELLO_C as the issue seems to have been fixed in OpenSSL v1.0.2c 20-FEB-2015 MGD SesolaNetAccept() SSL3_ST_SR_CLNT_HELLO_C in v1.0.2a bugfix; #if..#endif nesting of kludge described below 02-FEB-2015 MGD kludge; SesolaNetAccept() SSL3_ST_SR_CLNT_HELLO_C 29-JAN-2015 MGD bugfix; SesolaNetClientBegin() ambiguous WatchThis(SNI) 11-NOV-2014 MGD SesolaNetAccept() collect version, cipher, cached 04-OCT-2013 MGD SesolaNetThisIsSSL() support [ServiceNonSSLRedirect] 03-AUG-2013 MGD SesolaNetClientBegin() include SNI before connect 28-APR-2012 MGD bugfix; SesolaNetAccept() initialise value=0 bugfix; SesolaNetRead() SSL state not SSL_ST_OK bugfix; SesolaNetWrite() SSL state not SSL_ST_OK 04-SEP-2010 MGD Sesola_read_ast() extend HTTP methods check 18-JUL-2010 MGD bugfix; SSL_set_info_callback() not SSL_CTX_set..() 25-APR-2008 MGD increase sanity check error count from 255 to 8192 12-APR-2006 MGD bugfix; SSL_shutdown() problem reported by JPP introduce SesolaNetReadAst() and SesolaNetWriteAst() to defer reset of AST function address used to indicate AST-in-progress in other parts of the code 10-JUN-2005 MGD make EXQUOTA (particularly ASTLM) a little more obvious 14-APR-2005 MGD bugfix; SesolaNetClientShutdown() remove SSL_shutdown() (revealed by ->https: tunnelling shutdown) 21-DEC-2004 MGD bugfix; obscure in Sesesol_read() and Sesola_Write() when WATCHing via SSL due to undereferenced NULL 'rqptr' 17-DEC-2004 MGD bugfix; Sesola_read_ast() and Sesola_write_ast() zero I/O status block count on error status 14-DEC-2004 MGD remove BIO_set_retry_..() BIO_clear_retry_..(), add some sanity checking around reads and writes 17-NOV-2004 MGD bugfix; SesolaNetRead() and SesolaNetWrite() if no I/O and hence no defered AST reset defered AST function pointer (thanks to jpp@esme.fr for isolating this during BETA) 27-OCT-2004 MGD SesolaNetInProgress() raw I/O in progress? (for ProxyEnd()) 21-AUG-2004 MGD significant refinements to SSL processing 22-JUL-2004 MGD changes to SSL shutdown, SesolaNetEnd() to allow persistent connections over SSL for HTTP/1.1 (and general SSL performance improvements - long overdue) 29-JAN-2003 MGD bugfix; error recovery in Sesola_read() and Sesola_write() 18-APR-2002 MGD bugfix; service and client SSL contexts 26-FEB-2002 MGD bugfix; SesolaNetRequestEnd() wait for blocking I/O 08-JAN-2002 MGD rework SESOLA.C */ /*****************************************************************************/ #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 /* application header files */ #define SESOLA_REQUIRED #include "Sesola.h" #define WASD_MODULE "SESOLANET" /***************************************/ #ifdef SESOLA /* secure sockets layer */ /***************************************/ /******************/ /* global storage */ /******************/ #if OPENSSL_VERSION_NUMBER < 0x10100000L /* prior to OpenSSL v1.1.0 a simple structure initialisation */ struct bio_method_struct { int type; const char *name; int (*bwrite) (BIO *, const char *, int); int (*bread) (BIO *, char *, int); int (*bputs) (BIO *, const char *); int (*bgets) (BIO *, char *, int); long (*ctrl) (BIO *, int, long, void *); int (*create) (BIO *); int (*destroy) (BIO *); long (*callback_ctrl) (BIO *, int, bio_info_cb *); }; struct bio_method_struct Sesola_method = { BIO_TYPE_FD, "WASD Sesola", Sesola_netio_write, Sesola_netio_read, Sesola_puts, Sesola_gets, Sesola_ctrl, Sesola_create, Sesola_destroy, NULL }; #endif /********************/ /* external storage */ /********************/ extern int EfnWait, EfnNoWait, NetReadBufferSize, SesolaSNI, SesolaSSLversion; extern char *SesolaDefaultCertPtr, *SesolaDefaultCipherListPtr, *SesolaDefaultKeyPtr; extern char ErrorSanityCheck[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern WATCH_STRUCT Watch; #if OPENSSL_VERSION_NUMBER < 0x10100000L extern int (*Sesola_SSL_get_servername)(SSL*, int); #endif /*****************************************************************************/ /* Begin an SSL transaction for a request. Create the Sesola structure used to store HTTPd SSL-related used during the transaction, initialize the OpenSSL structures required, then begin the OpenSSL accept functionality. */ SesolaNetBegin (REQUEST_STRUCT *rqptr) { int value; SERVICE_STRUCT *svptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaNetBegin()"); /* note that this IS NOT HEAP memory, and must be explicitly VmFree()d */ sesolaptr = (SESOLA_STRUCT*)VmGet (sizeof(SESOLA_STRUCT)); sesolaptr->RequestPtr = rqptr; sesolaptr->WatchItem = rqptr->WatchItem; sesolaptr->NetIoPtr = rqptr->NetIoPtr; rqptr->NetIoPtr->SesolaPtr = sesolaptr; if (!((SESOLA_CONTEXT*)rqptr->ServicePtr->SSLserverPtr)->SslCtx) { ErrorNoticed (rqptr, 0, "SslCtx == NULL", FI_LI); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN SSL context NULL"); SesolaNetBeginFail (sesolaptr); return; } svptr = sesolaptr->NetIoPtr->ServicePtr; sesolaptr->SslCtx = (SSL_CTX*)((SESOLA_CONTEXT*)svptr->SSLserverPtr)->SslCtx; sesolaptr->SslPtr = SSL_new (sesolaptr->SslCtx); if (!sesolaptr->SslPtr) { ErrorNoticed (rqptr, 0, "SSL_new() failed", FI_LI); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN SSL_new() failed"); SesolaNetBeginFail (sesolaptr); return; } SSL_clear (sesolaptr->SslPtr); /* set the application data to point to the request structure */ SSL_set_app_data (sesolaptr->SslPtr, sesolaptr); sesolaptr->BioPtr = BIO_new (BIO_s_Sesola()); if (!sesolaptr->BioPtr) { ErrorNoticed (rqptr, 0, "BIO_new() failed", FI_LI); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN BIO_new() failed"); SesolaNetBeginFail (sesolaptr); return; } #if OPENSSL_VERSION_NUMBER < 0x10100000L sesolaptr->BioPtr->ptr = sesolaptr; #else BIO_set_data (sesolaptr->BioPtr, sesolaptr); BIO_set_init (sesolaptr->BioPtr, 1); BIO_set_shutdown (sesolaptr->BioPtr, 1); #endif /* set up the SSL filter */ sesolaptr->BioSslPtr = BIO_new (BIO_f_ssl()); if (!sesolaptr->BioSslPtr) { ErrorNoticed (rqptr, 0, "BIO_new() failed", FI_LI); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN BIO_new() failed"); SesolaNetBeginFail (sesolaptr); return; } SSL_set_bio (sesolaptr->SslPtr, sesolaptr->BioPtr, sesolaptr->BioPtr); SSL_set_accept_state (sesolaptr->SslPtr); #if WATCH_CAT if (WATCHING (rqptr, WATCH_SESOLA)) { WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN"); /* set for SSL information and verification callback */ SSL_set_ex_data (sesolaptr->SslPtr, 0, sesolaptr); SSL_set_info_callback (sesolaptr->SslPtr, &SesolaWatchInfoCallback); /* set for BIO callback with the request pointer as it's argument */ BIO_set_callback (sesolaptr->BioPtr, &SesolaWatchBioCallback); BIO_set_callback_arg (sesolaptr->BioPtr, sesolaptr); } #endif /* associate the request with the SSL structure */ sesolaptr->RequestPtr = rqptr; /* begin the SSL handshake */ SesolaNetAccept (sesolaptr); } /*****************************************************************************/ /* Either SesolaNetBegin() or SesolaNetAccept() have failed in some way, the TLS/SSL negotiation has not occured, the TLS/SSL connection is not established and the request has not got underway. Shut down the request in as economical way as possible. */ void SesolaNetBeginFail (SESOLA_STRUCT *sesolaptr) { REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = sesolaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaNetBeginFail()"); if (WATCHING (rqptr, WATCH_CONNECT)) WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "CLOSE !AZ,!UL !AZ", rqptr->ClientPtr->Lookup.HostName, rqptr->ClientPtr->IpPort, rqptr->ServicePtr->ServerHostPort); rqptr->NetIoPtr->SesolaPtr = NULL; SesolaNetFree (sesolaptr); RequestEnd4 (rqptr); } /*****************************************************************************/ /* This establishes the connection with an SSL client by providing the server "hello", certificate and key exchange, etc. Due to the non-blocking I/O used by WASD this function will be called multiple times to complete the OpenSSL accept. */ SesolaNetAccept (SESOLA_STRUCT *sesolaptr) { int value = 0; REQUEST_STRUCT *rqptr; SSL_SESSION *SessionPtr; /*********/ /* begin */ /*********/ rqptr = sesolaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaNetAccept() !&F !UL", &SesolaNetAccept, sesolaptr->SslAcceptCount); sesolaptr->SslStateFunction = &SesolaNetAccept; value = SSL_accept (sesolaptr->SslPtr); if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SSL_accept() !SL !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); /* just a sanity check on the SSL_accept() activity */ if (sesolaptr->SslAcceptCount++ > SESOLA_SSL_ACCEPT_MAX) { if (rqptr->NetIoPtr->Channel) { /* break the connection */ sys$dassgn (rqptr->NetIoPtr->Channel); rqptr->NetIoPtr->Channel = 0; } value = -1; } /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; sesolaptr->SslStateFunction = NULL; if (sesolaptr->HTTPduringHandshake) SesolaNetThisIsSSL (sesolaptr); if (value <= 0) { /********/ /* fail */ /********/ if (WATCHING (rqptr, WATCH_SESOLA)) { WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN failed"); SesolaWatchErrors (sesolaptr); } SesolaNetBeginFail (sesolaptr); return; } /***********/ /* TLS/SSL */ /***********/ sesolaptr->ClientCertPtr = SSL_get_peer_certificate (sesolaptr->SslPtr); if (WATCHING (rqptr, WATCH_SESOLA)) SesolaWatchSession (sesolaptr); if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "->ClientCertPtr !&X", sesolaptr->ClientCertPtr); RequestBegin (rqptr); } /*****************************************************************************/ /* Shutdown the SSL session with the client (elegantly if we can!). Once kicked off this function becomes autonomous, eventually deassigning the socket and freeing both the network I/O structure and itself. The calling routine can just continue on its way. */ void SesolaNetEnd (SESOLA_STRUCT *sesolaptr) { int value; NETIO_STRUCT *ioptr; /*********/ /* begin */ /*********/ ioptr = sesolaptr->NetIoPtr; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SesolaNetEnd() !&F chan:!UL count:!UL \ read:%X!8XL AST:!&A inprog:!&B write:%X!8XL AST:!&A inprog:!&B", &SesolaNetEnd, ioptr->Channel, sesolaptr->SslShutdownCount, ioptr->ReadStatus, ioptr->ReadAstFunction, sesolaptr->ReadInProgress, ioptr->WriteStatus, ioptr->WriteAstFunction, sesolaptr->WriteInProgress); if (sesolaptr->SslStateFunction && sesolaptr->SslStateFunction != &SesolaNetEnd) { /* original SSL_accept() has not completed */ if (WATCHING (ioptr, WATCH_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_SESOLA, "BEGIN failed"); sys$dassgn (ioptr->Channel); ioptr->Channel = 0; } /* everything (now) is coming here! */ sesolaptr->SslStateFunction = &SesolaNetEnd; if (ioptr->Channel && VMSok(ioptr->ReadStatus) && VMSok(ioptr->WriteStatus)) { if (!sesolaptr->SslShutdownCount++) { if (WATCHING (ioptr, WATCH_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_SESOLA, "SHUTDOWN"); /* not going to wait for the client to respond */ SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN); value = SSL_shutdown (sesolaptr->SslPtr); if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SSL_shutdown() !SL !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); } else if (sesolaptr->SslShutdownCount > SESOLA_SSL_SHUTDOWN_MAX) { /* sanity check, so knock the socket on the head */ if (ioptr->Channel) { sys$dassgn (ioptr->Channel); ioptr->Channel = 0; } ErrorNoticed (NULL, SS$_BUGCHECK, "SESOLA_SSL_SHUTDOWN_MAX", FI_LI); } } if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "reading:!&B writing:!&B", sesolaptr->ReadInProgress, sesolaptr->WriteInProgress); /* intercept any outstanding I/O */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; if (WATCHING (ioptr, WATCH_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_SESOLA, "END"); if (WATCHING (ioptr, WATCH_CONNECT)) WatchThis (WATCHITM(ioptr), WATCH_CONNECT, "CLOSE !AZ,!UL !AZ", ioptr->ClientPtr->Lookup.HostName, ioptr->ClientPtr->IpPort, ioptr->ServicePtr->ServerHostPort); if (ioptr->Channel) sys$dassgn (ioptr->Channel); VmFree (ioptr, FI_LI); SesolaNetFree (sesolaptr); } /*****************************************************************************/ /* Free the OpenSSL structures outside of any use of them (early mistake)! */ void* SesolaNetFree (SESOLA_STRUCT *sesolaptr) { int value; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaNetFree()"); if (sesolaptr->VerifyPeerDataSize) VmFree (sesolaptr->VerifyPeerDataPtr, FI_LI); if (sesolaptr->SslPtr) SSL_free (sesolaptr->SslPtr); VmFree (sesolaptr, FI_LI); return (NULL); } /*****************************************************************************/ /* Begin an SSL transaction. Create the Sesola structure used to store HTTPd SSL-related used during the transaction, initialize the OpenSSL structures required, then begin the OpenSSL connect functionality. Note that this is proxy functionality and so proxy request rundown functions are used. */ SesolaNetClientBegin (PROXY_TASK *tkptr) { int value; REQUEST_STRUCT *rqptr; SERVICE_STRUCT *svptr; SESOLA_CONTEXT *scptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHMOD (tkptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_MOD_SESOLA, "SesolaNetClientBegin()"); rqptr = tkptr->RequestPtr; svptr = tkptr->ServicePtr; if (!svptr->SSLclientPtr) { if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "CLIENT SSL not configured"); ProxyEnd (tkptr); return; } if (!((SESOLA_CONTEXT*)svptr->SSLclientPtr)->SslCtx) { ErrorNoticed (rqptr, 0, "SslCtx == NULL", FI_LI); if (WATCHING (rqptr, WATCH_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN SSL context NULL"); ProxyEnd (tkptr); return; } /* note that this IS NOT HEAP memory, and must be explicitly VmFree()d */ sesolaptr = (SESOLA_STRUCT*)VmGet (sizeof(SESOLA_STRUCT)); tkptr->NetIoPtr->SesolaPtr = sesolaptr; sesolaptr->NetIoPtr = tkptr->NetIoPtr; sesolaptr->ProxyTaskPtr = tkptr; sesolaptr->SslPtr = SSL_new (((SESOLA_CONTEXT*)svptr->SSLclientPtr)->SslCtx); if (!sesolaptr->SslPtr) { if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "BEGIN SSL_new() failed"); ErrorNoticed (rqptr, 0, "SSL_new() failed", FI_LI); if (rqptr) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI); } SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } SSL_clear (sesolaptr->SslPtr); scptr = (SESOLA_CONTEXT*)svptr->SSLclientPtr; if (scptr->CipherListPtr[0]) SSL_set_cipher_list (sesolaptr->SslPtr, scptr->CipherListPtr); /* set the application data to point to the proxy task structure */ SSL_set_app_data (sesolaptr->SslPtr, tkptr); sesolaptr->BioPtr = BIO_new (BIO_s_Sesola()); if (!sesolaptr->BioPtr) { if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "BEGIN BIO_new() failed"); ErrorNoticed (rqptr, 0, "BIO_new() failed", FI_LI); if (rqptr) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI); } SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } #if OPENSSL_VERSION_NUMBER < 0x10100000L sesolaptr->BioPtr->ptr = sesolaptr; #else BIO_set_data (sesolaptr->BioPtr, sesolaptr); BIO_set_init (sesolaptr->BioPtr, 1); BIO_set_shutdown (sesolaptr->BioPtr, 1); #endif /* set up the SSL filter */ sesolaptr->BioSslPtr = BIO_new (BIO_f_ssl()); if (!sesolaptr->BioSslPtr) { if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "BEGIN BIO_new() failed"); ErrorNoticed (rqptr, 0, "BIO_new() failed", FI_LI); if (rqptr) { rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI); } SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } SSL_set_bio (sesolaptr->SslPtr, sesolaptr->BioPtr, sesolaptr->BioPtr); SSL_set_connect_state (sesolaptr->SslPtr); /* Server Name Indication (SNI) */ #if OPENSSL_VERSION_NUMBER < 0x10100000L if (SesolaSNI && Sesola_SSL_get_servername) #else if (SesolaSNI) #endif { /* if the SSL_get_servername() exists then this control should work */ value = SSL_set_tlsext_host_name (sesolaptr->SslPtr, tkptr->RequestHostName); if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "SNI !AZ!AZ", tkptr->RequestHostName, value ? "" : " FAILED"); } if (scptr->VerifyCA) SSL_set_verify (sesolaptr->SslPtr, SSL_VERIFY_CLIENT_ONCE, &SesolaCertVerifyCallback); /* provide the sesola pointer for the verify callback */ SSL_set_ex_data (sesolaptr->SslPtr, 0, sesolaptr); #if WATCH_CAT if (WATCHING (rqptr, WATCH_SESOLA)) { WatchThis (WATCHITM(rqptr), WATCH_SESOLA, "BEGIN"); /* set for SSL information and verification callback */ SSL_set_info_callback (sesolaptr->SslPtr, &SesolaWatchInfoCallback); /* set for BIO callback with the request pointer as it's argument */ BIO_set_callback (sesolaptr->BioPtr, &SesolaWatchBioCallback); BIO_set_callback_arg (sesolaptr->BioPtr, sesolaptr); } #endif /* associate the proxy task with the SSL structure */ sesolaptr->ProxyTaskPtr = tkptr; /* begin the SSL handshake */ SesolaNetClientConnect (sesolaptr); } /*****************************************************************************/ /* This establishes the connection to an SSL server by providing the server "hello", certificate and key exchange, etc. Due to the non-blocking I/O used by WASD this function will be called multiple times to complete the OpenSSL connect. */ SesolaNetClientConnect (SESOLA_STRUCT *sesolaptr) { int status, value; PROXY_TASK *tkptr; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ /* get the associate proxy task and request pointers */ tkptr = sesolaptr->ProxyTaskPtr; rqptr = tkptr->RequestPtr; if (WATCHMOD (tkptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_MOD_SESOLA, "SesolaNetClientConnect() !&F !UL", &SesolaNetClientConnect, sesolaptr->SslConnectCount); sesolaptr->SslStateFunction = &SesolaNetClientConnect; /* just a sanity check on the SSL_connect() activity */ if (sesolaptr->SslConnectCount++ < SESOLA_SSL_CONNECT_MAX) { if (sesolaptr->HTTPduringHandshake) { rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_501), FI_LI); SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } value = SSL_connect (sesolaptr->SslPtr); if (WATCHMOD (tkptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_MOD_SESOLA, "SSL_connect() !SL", value); /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; if (sesolaptr->HTTPduringHandshake) { rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_501), FI_LI); SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } } /* can't free structures while non-blocking I/O still outstanding */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) { /* so knock the socket on the head */ if (sesolaptr->NetIoPtr->Channel) ProxyNetCloseSocket (tkptr); return; } if (value <= 0) { /*********/ /* error */ /*********/ if (WATCHING (rqptr, WATCH_SESOLA)) SesolaWatchErrors (sesolaptr); if (rqptr) { rqptr->rqResponse.HttpStatus = 502; if (sesolaptr->CertVerifyFailed) /* or perhaps some more formal message? */ ErrorGeneral (rqptr, "Unknown server Certificate Authority.", FI_LI); else ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_502), FI_LI); } SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } if (WATCHING (tkptr, WATCH_SESOLA)) SesolaWatchSession (sesolaptr); sesolaptr->SslStateFunction = NULL; if (tkptr->ProxyTunnel) ProxyTunnelBegin (tkptr); else ProxyWriteRequest (tkptr); } /*****************************************************************************/ /* Shutdown the SSL session with the client (via an SSL alert so that we don't wait around for a response!) Return true if RequestEnd() can continue the request run-down, false if has to abort and wait for some more to happen! */ SesolaNetClientShutdown (SESOLA_STRUCT *sesolaptr) { int value; PROXY_TASK *tkptr; /*********/ /* begin */ /*********/ tkptr = sesolaptr->ProxyTaskPtr; if (WATCHMOD (tkptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_MOD_SESOLA, "SesolaNetClientShutdown() !&F !UL !UL !&B !&B", &SesolaNetClientShutdown, sesolaptr->NetIoPtr->Channel, sesolaptr->SslShutdownCount, sesolaptr->ReadInProgress, sesolaptr->WriteInProgress); /* everything is now going to be coming here! */ sesolaptr->SslStateFunction = &SesolaNetClientShutdown; /* only provide explicit shutdown if still connected! */ if (sesolaptr->NetIoPtr->Channel) { /* just a sanity check on the SSL_shutdown() activity */ if (!sesolaptr->SslShutdownCount++) { if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "SHUTDOWN"); /* not going to wait for the server to respond */ SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN); } else if (sesolaptr->SslShutdownCount > SESOLA_SSL_SHUTDOWN_MAX) { /* sanity check, so knock the socket on the head */ if (sesolaptr->NetIoPtr->Channel) ProxyNetCloseSocket (tkptr); } } /* intercept any outstanding I/O */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; if (WATCHING (tkptr, WATCH_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_SESOLA, "END"); sesolaptr->SslStateFunction = NULL; SesolaNetClientFree (tkptr); ProxyEnd (tkptr); } /*****************************************************************************/ /* Free the OpenSSL structures outside of any use of them (early mistake)! */ SesolaNetClientFree (PROXY_TASK *tkptr) { int value; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHMOD (tkptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(tkptr), WATCH_MOD_SESOLA, "SesolaNetClientFree()"); sesolaptr = (SESOLA_STRUCT*)tkptr->NetIoPtr->SesolaPtr; if (sesolaptr->SslPtr) SSL_free (sesolaptr->SslPtr); VmFree (sesolaptr, FI_LI); tkptr->NetIoPtr->SesolaPtr = NULL; } /*****************************************************************************/ /* Plain-text HTTP has been detected arriving on an SSL service. Return either an error message or redirect response (if configured). The redirect parameter can comprise an optional leading HTTP response code 301, 302, or 307 (default), followed by an optional scheme ("http://" or "https://"), optional host name or IP address, and optional port number (port must have leading colon if only parameter), and even an optional URI. The minimum parameter is a single colon, which redirects to the same service name on port 80. A 307 is used to indicate to the agent that a non-GET should not be turned into one! Something that apparently commonly (incorrectly) happens with 302s. */ SesolaNetThisIsSSL (SESOLA_STRUCT *sesolaptr) { static char ThisIsSSLFao [] = "HTTP/1.0 400 This is an SSL service!!\r\n\ Server: !AZ\r\n\ Content-Type: text/html\r\n\ \r\n\ 400 error - This is an SSL service!!\n"; static char RedirectFao [] = "HTTP/1.0 !UL Redirection\r\n\ Server: !AZ\r\n\ Location: !AZ://!AZ!AZ\r\n\ Content-Type: text/html\r\n\ \r\n\ !UL redirection - !AZ://!AZ!AZ\n"; int code, status; unsigned short slen; char *cptr, *pptr, *sptr, *zptr; char NonSslRedirect [256], ResponseBuffer [4096]; REQUEST_STRUCT *rqptr; IO_SB IOsb; /*********/ /* begin */ /*********/ rqptr = sesolaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA, "SesolaNetThisIsSSL()"); if (!rqptr) return; for (cptr = rqptr->ServicePtr->NonSslRedirect; *cptr == ' '; cptr++); if (*cptr) { /************/ /* redirect */ /************/ /* buffer the directive */ for (cptr = rqptr->ServicePtr->NonSslRedirect; *cptr == ' '; cptr++); zptr = (sptr = NonSslRedirect) + sizeof(NonSslRedirect)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; cptr = NonSslRedirect; if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "REDIRECT !AZ", cptr); /* optional HTTP response code - default is 307 */ if (isdigit(*cptr)) { code = atoi(cptr); if (code != 301 && code != 302 && code != 303) code = 307; while (*cptr && (isdigit(*cptr) || ISLWS(*cptr))) cptr++; } else code = 307; pptr = "http"; sptr = ""; if (*cptr == ':') { /* default to current service name */ sptr = rqptr->ServicePtr->ServerHostName; /* if no trailing number then no port - default will be 80 */ if (!isdigit(*(cptr+1))) cptr++; } else if (MATCH8 (cptr, "https://")) { pptr = "https"; cptr += 8; } else if (MATCH7 (cptr, "http://")) cptr += 7; status = FaoToBuffer (ResponseBuffer, sizeof(ResponseBuffer)-1, &slen, RedirectFao, code, SoftwareID, pptr, sptr, cptr, code, pptr, sptr, cptr); rqptr->rqResponse.HttpStatus = code; } else { /***********/ /* message */ /***********/ status = FaoToBuffer (ResponseBuffer, sizeof(ResponseBuffer)-1, &slen, ThisIsSSLFao, SoftwareID); rqptr->rqResponse.HttpStatus = 400; } if (VMSnok(status)) ErrorNoticed (rqptr, status, "FaoToBuffer()", FI_LI); /*******************/ /* write to client */ /*******************/ if (WATCHPNT(rqptr) && (WATCH_CATEGORY(WATCH_RESPONSE_HEADER) || WATCH_CATEGORY(WATCH_RESPONSE_BODY))) WatchData (ResponseBuffer, slen); sys$qiow (EfnWait, sesolaptr->NetIoPtr->Channel, IO$_WRITEVBLK, &IOsb, 0, 0, ResponseBuffer, slen, 0, 0, 0, 0); } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ BIO_METHOD *BIO_s_Sesola() { BIO_METHOD *bmptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "BIO_s_Sesola()"); #if OPENSSL_VERSION_NUMBER < 0x10100000L return (&Sesola_method); #else bmptr = BIO_meth_new (BIO_TYPE_FD, "WASD Sesola"); BIO_meth_set_write (bmptr, Sesola_netio_write); BIO_meth_set_read (bmptr, Sesola_netio_read); BIO_meth_set_puts (bmptr, Sesola_puts); BIO_meth_set_gets (bmptr, Sesola_gets); BIO_meth_set_ctrl (bmptr, Sesola_ctrl); BIO_meth_set_create (bmptr, Sesola_create); BIO_meth_set_destroy (bmptr, Sesola_destroy); return (bmptr); #endif } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ int Sesola_create (BIO *bioptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "Sesola_create()"); #if OPENSSL_VERSION_NUMBER < 0x10100000L bioptr->init = 1; bioptr->shutdown = 1; bioptr->num = 0; bioptr->flags = 0; #endif return (1); } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ int Sesola_destroy (BIO *bioptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "Sesola_destroy()"); if (!bioptr) return (0); /* don't free anything, the BIO pointer is set to the Sesola structure */ #if OPENSSL_VERSION_NUMBER < 0x10100000L bioptr->ptr = NULL; #else BIO_set_data (bioptr, NULL); #endif return (1); } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ long Sesola_ctrl ( BIO *bioptr, int Command, long Number, char *Pointer ) { int value; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "Sesola_ctrl() !UL !UL !&X", Command, Number, Pointer); value = 1; switch (Command) { case BIO_CTRL_RESET: return (1); case BIO_CTRL_EOF: return (1); case BIO_CTRL_SET: #if OPENSSL_VERSION_NUMBER < 0x10100000L bioptr->num = Number; #else BIO_set_init(bioptr, Number); #endif return (1); case BIO_CTRL_SET_CLOSE: return (1); case BIO_CTRL_FLUSH: return (1); case BIO_CTRL_DUP: return (1); case BIO_CTRL_GET_CLOSE: return (0); case BIO_CTRL_INFO: return (0); case BIO_CTRL_GET: return (0); case BIO_CTRL_PENDING: return (0); case BIO_CTRL_POP: return (0); case BIO_CTRL_PUSH: return (0); case BIO_CTRL_WPENDING: return (0); default: return (0); } } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ int Sesola_gets ( BIO *bioptr, char *StringPtr, int StringSize ) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "Sesola_gets() !&X !UL", StringPtr, StringSize); return(0); } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ int Sesola_puts ( BIO *bioptr, char *StringPtr ) { int Length; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "Sesola_puts() !&Z", StringPtr); if (!StringPtr) return (0); Length = strlen (StringPtr); if (Length == 0) return (0); return (Sesola_netio_write (bioptr, StringPtr, Length)); } /*****************************************************************************/ /* 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 */ /*********************/ /* external storage */ extern char ErrorSanityCheck[]; SesolaNetBegin (REQUEST_STRUCT *rqptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientBegin (PROXY_TASK *tkptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientShutdown (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetEnd (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } Sesola_read_ast (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } Sesola_write_ast (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetAccept (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientConnect (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } /************************/ #endif /* ifdef SESOLA */ /************************/ /*****************************************************************************/