/*****************************************************************************/
/*
                                 SesolaCGI.c

Secure Sockets Layer CGI variables.


VERSION HISTORY
---------------
15-MAR-2020  MGD  add SSL_TLS_ALPN variable for ALPN protocol list
23-JUL-2016  MGD  use SSL_SESSION_get_id() instead of ->session_id..
27-SEP-2015  MGD  SesolaCgiVariablesExtension() document X509 extensions
28-JUL-2013  MGD  add Apache SSL_TLS_SNI variable for SNI server name
21-OCT-2001  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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* VMS related header files */
#include <ssdef.h>
#include <stsdef.h>

/* application header files */
#define SESOLA_REQUIRED
#include "Sesola.h"

#define WASD_MODULE "SESOLACGI"

/***************************************/
#ifdef SESOLA  /* secure sockets layer */
/***************************************/

/********************/
/* external storage */
/********************/

#ifdef DBUG
extern BOOL Debug;
#else
#define Debug 0 
#endif

extern int  ToLowerCase[],
            ToUpperCase[];

extern char  ErrorSanityCheck[],
             SoftwareID[];

extern BIO  *SesolaBioMemPtr;

extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
Generate appropriate SSL-related CGI variables.
Called from CgiGenerateVariables().
*/ 

int SesolaCgiGenerateVariables
(
REQUEST_STRUCT *rqptr,
int VarType
)
{
   static $DESCRIPTOR (NumberFaoDsc, "!UL\0");

   int  status,
        KeySize;
   char  String [512];
   char  *cptr;
   $DESCRIPTOR (StringDsc, String);
   SESOLA_STRUCT  *sesolaptr;
   SSL  *SslPtr;
   SSL_CIPHER  *CipherPtr;
   X509  *ServerCertPtr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                 "SesolaCgiGenerateVariables() !UL", VarType);

   if (!(sesolaptr = SesolaRequestSesolaPtr (rqptr))) return (SS$_NORMAL);

   SslPtr = sesolaptr->SslPtr;
   CipherPtr = SSL_get_current_cipher (SslPtr);
   ServerCertPtr = SSL_get_certificate (SslPtr);

   status =  SS$_NORMAL;
   if (sesolaptr->ClientCertPtr)
   {
      if (rqptr->rqAuth.ClientCertFingerprintPtr &&
         rqptr->rqAuth.ClientCertFingerprintPtr[0])
         status = CgiVariable (rqptr, "AUTH_X509_FINGERPRINT",
                               rqptr->rqAuth.ClientCertFingerprintPtr,
                               VarType);

      if (VMSok (status))
         if (rqptr->rqAuth.ClientCertIssuerPtr &&
             rqptr->rqAuth.ClientCertIssuerPtr[0])
            status = CgiVariable (rqptr, "AUTH_X509_ISSUER",
                                  rqptr->rqAuth.ClientCertIssuerPtr, VarType);
   
      if (VMSok (status))
         if (rqptr->rqAuth.ClientCertSubjectPtr &&
             rqptr->rqAuth.ClientCertSubjectPtr[0])
            status = CgiVariable (rqptr, "AUTH_X509_SUBJECT",
                                  rqptr->rqAuth.ClientCertSubjectPtr, VarType);

      if (VMSok (status))
      {
         cptr = (char*)SSL_CIPHER_get_name (CipherPtr);
         status = CgiVariable (rqptr, "AUTH_X509_CIPHER", cptr, VarType);
      }

      if (VMSok (status))
      {
         KeySize = SSL_CIPHER_get_bits (CipherPtr, NULL);
         sys$fao (&NumberFaoDsc, 0, &StringDsc, KeySize);
         status = CgiVariable (rqptr, "AUTH_X509_KEYSIZE", String, VarType);
      }
   }

   if (VMSok (status))
   {
      BIO_reset (SesolaBioMemPtr);
      switch (rqptr->rqPathSet.SSLCGIvar)
      {
         case SESOLA_CGI_VAR_APACHE_MOD_SSL :
         case SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT :
         case SESOLA_CGI_VAR_APACHE_MOD_SSL_EXTENS :
         case SESOLA_CGI_VAR_APACHE_MOD_SSL_OID :
            status = SesolaCgiVariablesApacheModSsl (rqptr, VarType);
            break;
         case SESOLA_CGI_VAR_PURVEYOR :
            status = SesolaCgiVariablesPurveyor (rqptr, VarType);
            break;
      }
      BIO_reset (SesolaBioMemPtr);
   }

   return (status);
}

/*****************************************************************************/
/*
Generate some Apache mod_SSL style CGI variables.
*/ 

int SesolaCgiVariablesApacheModSsl
(
REQUEST_STRUCT *rqptr,
int VarType
)
{
   static char  HexDigits [] = "0123456789abcdef";
   static $DESCRIPTOR (NumberFaoDsc, "!UL\0");

   int  cnt, status,
        AlgKeySize,
        ObjNid,
        UseKeySize;
   char  *cptr, *sptr, *zptr,
         *CurrentRecordPtr;
   char  CertFingerprint [64],
         String [512],
         VariableName [64],
         VariableValue [256];
   $DESCRIPTOR (StringDsc, String);
   $DESCRIPTOR (VariableNameDsc, VariableName);
   SESOLA_STRUCT  *sesolaptr;
   SSL  *SslPtr;
   SSL_CIPHER  *CipherPtr;
   SSL_SESSION  *SessionPtr;
   X509  *ClientCertPtr,
         *ServerCertPtr;
   ASN1_OBJECT *Asn1Ptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                 "SesolaCgiVariablesApacheModSsl()");

   sesolaptr = NULL;
   if (HTTP2_REQUEST(rqptr))
      sesolaptr = (SESOLA_STRUCT*)rqptr->Http2Stream.Http2Ptr->NetIoPtr->SesolaPtr; 
   if (!sesolaptr) sesolaptr = (SESOLA_STRUCT*)rqptr->NetIoPtr->SesolaPtr;

   SslPtr = sesolaptr->SslPtr;
   SessionPtr = SSL_get_session (SslPtr);
   CipherPtr = SSL_get_current_cipher (SslPtr);
   ServerCertPtr = SSL_get_certificate (SslPtr);

   if (!SessionPtr || !CipherPtr || !ServerCertPtr)
   {
      if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
         WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                    "!&X !&X !&X", SessionPtr, CipherPtr, ServerCertPtr);
      ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
      return (SS$_BUGCHECK);
   }

   /***************/
   /* basic stuff */
   /***************/

   if (VMSnok (status =
       CgiVariable (rqptr, "HTTPS", "true", VarType)))
      return (status);

   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_PROTOCOL",
                    (char*)SSL_get_version(SslPtr), VarType)))
      return (status);

   cptr = SSL_SESSION_get_id (SessionPtr, &cnt);
   zptr = (sptr = String) + sizeof(String)-1;
   if (cnt)
   {
      while (cnt--)
      {
         if (sptr >= zptr) break;
         *sptr++ = HexDigits[(*cptr >> 4) & 0x0f];
         if (sptr >= zptr) break;
         *sptr++ = HexDigits[*cptr++ & 0x0f];
      }
      *sptr = '\0';
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SESSION_ID", String, VarType)))
         return (status);
   }

   cptr = (char*)SSL_CIPHER_get_name (CipherPtr);
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CIPHER", cptr, VarType)))
      return (status);

   UseKeySize = SSL_CIPHER_get_bits(CipherPtr, &AlgKeySize);

   /* same as "mod_SSL" v2.5 */
   if (UseKeySize < 56)
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CIPHER_EXPORT", "true", VarType)))
         return (status);

   sys$fao (&NumberFaoDsc, 0, &StringDsc, UseKeySize);
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CIPHER_USEKEYSIZE", String, VarType)))
      return (status);

   sys$fao (&NumberFaoDsc, 0, &StringDsc, AlgKeySize);
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CIPHER_ALGKEYSIZE", String, VarType)))
      return (status);

   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_VERSION_INTERFACE",
                            APACHE_MOD_SSL_VERSION_INTERFACE, VarType)))
      return (status);

   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_VERSION_LIBRARY",
                    (char*)OpenSSL_version(OPENSSL_VERSION), VarType)))
      return (status);

   if (sesolaptr->SNIServerName[0])
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_TLS_SNI",
                       sesolaptr->SNIServerName, VarType)))
         return (status);

   if (sesolaptr->ALPNok)
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_TLS_ALPN", sesolaptr->ALPNok, VarType)))
         return (status);

   /****************/
   /* client stuff */
   /****************/

   if (ClientCertPtr = sesolaptr->ClientCertPtr)
   {
      sys$fao (&NumberFaoDsc, 0, &StringDsc, X509_get_version(ClientCertPtr)+1);
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_M_VERSION", String, VarType)))
         return (status);

      i2a_ASN1_INTEGER (SesolaBioMemPtr, X509_get_serialNumber(ClientCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_M_SERIAL", String, VarType)))
         return (status);

      X509_NAME_oneline (X509_get_subject_name(ClientCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_S_DN", String, VarType)))
         return (status);

      sptr = VariableName;
      for (cptr = "SSL_CLIENT_S_DN_"; *cptr; *sptr++ = *cptr++);
      for (;;)
      {
         cptr = SesolaCertParseDn (String, NULL);
         if (!cptr) break;
         if (*cptr == '/') cptr++;
         zptr = (sptr = VariableName+16) + sizeof(VariableName)-17;
         while (*cptr && *cptr != '=' && sptr < zptr) *sptr++ = *cptr++;
         if (!*cptr) break;
         cptr++;
         *sptr = '\0';
         zptr = (sptr = VariableValue) + sizeof(VariableValue)-1;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (VMSnok (status =
             CgiVariable (rqptr, VariableName, VariableValue, VarType)))
            return (status);
      }

      X509_NAME_oneline (X509_get_issuer_name(ClientCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_I_DN", String, VarType)))
         return (status);

      sptr = VariableName;
      for (cptr = "SSL_CLIENT_I_DN_"; *cptr; *sptr++ = *cptr++);
      for (;;)
      {
         cptr = SesolaCertParseDn (String, NULL);
         if (!cptr) break;
         if (*cptr == '/') cptr++;
         zptr = (sptr = VariableName+16) + sizeof(VariableName)-17;
         while (*cptr && *cptr != '=' && sptr < zptr) *sptr++ = *cptr++;
         if (!*cptr) break;
         cptr++;
         *sptr = '\0';
         zptr = (sptr = VariableValue) + sizeof(VariableValue)-1;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (VMSnok (status =
             CgiVariable (rqptr, VariableName, VariableValue, VarType)))
            return (status);
      }

      ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notBefore(ClientCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_V_START", String, VarType)))
         return (status);

      ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(ClientCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_V_END", String, VarType)))
         return (status);

#if SESOLA_SINCE_110
      X509_ALGOR_get0 (&Asn1Ptr, NULL, NULL,
                       X509_get0_tbs_sigalg(ClientCertPtr));
      ObjNid = OBJ_obj2nid (Asn1Ptr);
#else
      ObjNid = OBJ_obj2nid (ClientCertPtr->cert_info->signature->algorithm);
#endif
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_A_SIG", 
             ObjNid == NID_undef ? "UNKNOWN" : (char*)OBJ_nid2ln(ObjNid),
             VarType)))
         return (status);

#if SESOLA_SINCE_110
      X509_PUBKEY_get0_param (&Asn1Ptr, NULL, 0, NULL,
                              X509_get_X509_PUBKEY(ClientCertPtr));
      ObjNid = OBJ_obj2nid (Asn1Ptr);
#else
      ObjNid = OBJ_obj2nid (ClientCertPtr->cert_info->key->algor->algorithm);
#endif
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_A_KEY",
             ObjNid == NID_undef ? "UNKNOWN" : (char*)OBJ_nid2ln(ObjNid),
             VarType)))
         return (status);
   }

#if APACHE_MOD_SSL_SERVER_CERT
   /*
      This variable is larger than can be support in a DCL symbol.
      It can be support in a CGIplus variable stream, but I imagine
      has a limited range of uses and so is not generated by default.
   */
   if (VarType == CGI_VARIABLE_STREAM)
   {
      PEM_write_bio_X509(SesolaBioMemPtr, ClientCertPtr);
      BIO_read (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_CERT", String, VarType)))
         return (status);
   }
#endif

   /****************/
   /* server stuff */
   /****************/

   if (rqptr->rqPathSet.SSLCGIvar != SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT)
   {
      sys$fao (&NumberFaoDsc, 0, &StringDsc, X509_get_version(ServerCertPtr)+1);
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_M_VERSION", String, VarType)))
         return (status);

      i2a_ASN1_INTEGER (SesolaBioMemPtr, X509_get_serialNumber(ServerCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_M_SERIAL", String, VarType)))
         return (status);

      X509_NAME_oneline (X509_get_subject_name(ServerCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_S_DN", String, VarType)))
         return (status);

      sptr = VariableName;
      for (cptr = "SSL_SERVER_S_DN_"; *cptr; *sptr++ = *cptr++);
      for (;;)
      {
         cptr = SesolaCertParseDn (String, NULL);
         if (!cptr) break;
         if (*cptr == '/') cptr++;
         zptr = (sptr = VariableName+16) + sizeof(VariableName)-17;
         while (*cptr && *cptr != '=' && sptr < zptr) *sptr++ = *cptr++;
         if (!*cptr) break;
         cptr++;
         *sptr = '\0';
         zptr = (sptr = VariableValue) + sizeof(VariableValue)-1;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (VMSnok (status =
             CgiVariable (rqptr, VariableName, VariableValue, VarType)))
            return (status);
      }

      X509_NAME_oneline (X509_get_issuer_name(ServerCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_I_DN", String, VarType)))
         return (status);

      sptr = VariableName;
      for (cptr = "SSL_SERVER_I_DN_"; *cptr; *sptr++ = *cptr++);
      for (;;)
      {
         cptr = SesolaCertParseDn (String, NULL);
         if (!cptr) break;
         if (*cptr == '/') cptr++;
         zptr = (sptr = VariableName+16) + sizeof(VariableName)-17;
         while (*cptr && *cptr != '=' && sptr < zptr) *sptr++ = *cptr++;
         if (!*cptr) break;
         cptr++;
         *sptr = '\0';
         zptr = (sptr = VariableValue) + sizeof(VariableValue)-1;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (VMSnok (status =
             CgiVariable (rqptr, VariableName, VariableValue, VarType)))
            return (status);
      }

      ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notBefore(ServerCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_V_START", String, VarType)))
         return (status);

      ASN1_UTCTIME_print (SesolaBioMemPtr, X509_get_notAfter(ServerCertPtr));
      BIO_gets (SesolaBioMemPtr, String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_V_END", String, VarType)))
         return (status);

#if SESOLA_SINCE_110
      X509_ALGOR_get0 (&Asn1Ptr, NULL, NULL,
                       X509_get0_tbs_sigalg(ServerCertPtr));
      ObjNid = OBJ_obj2nid (Asn1Ptr);
#else
      ObjNid = OBJ_obj2nid (ServerCertPtr->cert_info->signature->algorithm);
#endif
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_A_SIG", 
             ObjNid == NID_undef ? "UNKNOWN" : (char*)OBJ_nid2ln(ObjNid),
             VarType)))
         return (status);

#if SESOLA_SINCE_110
      X509_PUBKEY_get0_param (&Asn1Ptr, NULL, 0, NULL,
                              X509_get_X509_PUBKEY(ServerCertPtr));
      ObjNid = OBJ_obj2nid (Asn1Ptr);
#else
      ObjNid = OBJ_obj2nid (ServerCertPtr->cert_info->key->algor->algorithm);
#endif
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_SERVER_A_KEY",
             ObjNid == NID_undef ? "UNKNOWN" : (char*)OBJ_nid2ln(ObjNid),
             VarType)))
         return (status);

#if APACHE_MOD_SSL_SERVER_CERT
      /*
         This variable is larger than can be support in a DCL symbol.
         It can be support in a CGIplus variable stream, but I imagine
         has a limited range of uses and so is not generated by default.
      */
      if (VarType == CGI_VARIABLE_STREAM)
      {
         PEM_write_bio_X509(SesolaBioMemPtr, ServerCertPtr);
         BIO_read (SesolaBioMemPtr, String, sizeof(String));
         if (VMSnok (status =
             CgiVariable (rqptr, "SSL_SERVER_CERT", String, VarType)))
            return (status);
      }
#endif
   }

   /*******************/
   /* X509 extensions */
   /*******************/

   if (rqptr->rqPathSet.SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT ||
       rqptr->rqPathSet.SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_EXTENS ||
       rqptr->rqPathSet.SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_OID)
      SesolaCgiVariablesExtension (rqptr, VarType);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Generate some Purveyor style SSL-related CGI variables.
*/ 

int SesolaCgiVariablesPurveyor
(
REQUEST_STRUCT *rqptr,
int VarType
)
{
   static $DESCRIPTOR (NumberFaoDsc, "!UL\0");

   int  status,
        KeySize;
   char  String [512];
   char  *cptr;
   $DESCRIPTOR (StringDsc, String);
   SESOLA_STRUCT  *sesolaptr;
   SSL  *SslPtr;
   SSL_CIPHER  *CipherPtr;
   SSL_SESSION  *SessionPtr;
   X509  *ServerCertPtr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                 "SesolaCgiVariablesPurveyor()");

   sesolaptr = NULL;
   if (HTTP2_REQUEST(rqptr))
      sesolaptr = (SESOLA_STRUCT*)rqptr->Http2Stream.Http2Ptr->NetIoPtr->SesolaPtr; 
   if (!sesolaptr) sesolaptr = (SESOLA_STRUCT*)rqptr->NetIoPtr->SesolaPtr;

   SslPtr = sesolaptr->SslPtr;
   SessionPtr = SSL_get_session (SslPtr);
   CipherPtr = SSL_get_current_cipher (SslPtr);
   ServerCertPtr = SSL_get_certificate (SslPtr);

   if (!SessionPtr || !CipherPtr || !ServerCertPtr)
   {
      if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
         WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                    "!&X !&X !&X", SessionPtr, CipherPtr, ServerCertPtr);
      ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
      return (SS$_BUGCHECK);
   }

   cptr = (char*)SSL_CIPHER_get_name (CipherPtr);
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CIPHER", cptr, VarType)))
      return (status);

   KeySize = SSL_CIPHER_get_bits (CipherPtr, NULL);
   sys$fao (&NumberFaoDsc, 0, &StringDsc, KeySize);
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CIPHER_KEYSIZE", String, VarType)))
      return (status);

   if (!sesolaptr->ClientCertPtr)
   {
      if (VMSnok (status =
         CgiVariable (rqptr, "SSL_CLIENT_CA", "NONE", VarType)))
         return (status);

      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_DN", "NONE", VarType)))
         return (status);
   }
   else
   {
      X509_NAME_oneline (X509_get_issuer_name(sesolaptr->ClientCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_CA", String, VarType)))
         return (status);

      X509_NAME_oneline (X509_get_subject_name(sesolaptr->ClientCertPtr),
                         String, sizeof(String));
      if (VMSnok (status =
          CgiVariable (rqptr, "SSL_CLIENT_DN", String, VarType)))
         return (status);
   }

   if (rqptr->RemoteUser[0] && rqptr->rqAuth.SourceRealm == AUTH_SOURCE_X509)
      cptr = "TRUE";
   else
      cptr = "FALSE";
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_CLIENT_AUTHENTICATED", cptr, VarType)))
      return (status);

   if (VMSnok (status =
       CgiVariable (rqptr, "SECURITY_STATUS", "SSL", VarType)))
      return (status);

   X509_NAME_oneline (X509_get_issuer_name(ServerCertPtr),
                      String, sizeof(String));
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_SERVER_CA", String, VarType)))
      return (status);

   X509_NAME_oneline (X509_get_subject_name(ServerCertPtr),
                      String, sizeof(String));
   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_SERVER_DN", String, VarType)))
      return (status);

   if (VMSnok (status =
       CgiVariable (rqptr, "SSL_VERSION",
                    (char*)SSL_get_version(SslPtr), VarType)))
      return (status);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Generate X509 extension CGI variables.
*/ 

int SesolaCgiVariablesExtension
(
REQUEST_STRUCT *rqptr,
int VarType
)
{
   /* see also SesolaCertExtensions() */
   static char  *ShortHand [] = {
      "X509V3_SUBJECT_ALTERNATIVE_NAME", "X509V3_SAN",
      "X509V3_SUBJECT_ALTERNATIVE_NAME_USERPRINCIPALNAME", "X509V3_SAN_UPN",
      "X509V3_SUBJECT_ALTERNATIVE_NAME_RFC822NAME", "X509V3_SAN_822",
      NULL };

   int  cnt, idx, status;
   char  *aptr, *cptr, *sptr, *zptr;
   char  NameValue [256+2048];
   SESOLA_STRUCT  *sesolaptr;
   SSL  *SslPtr;
   X509  *CertPtr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                 "SesolaCgiVariablesExtension()");

   sesolaptr = NULL;
   if (HTTP2_REQUEST(rqptr))
      sesolaptr = (SESOLA_STRUCT*)rqptr->Http2Stream.Http2Ptr->NetIoPtr->SesolaPtr; 
   if (!sesolaptr) sesolaptr = (SESOLA_STRUCT*)rqptr->NetIoPtr->SesolaPtr;

   if (rqptr->rqPathSet.SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT)
      cnt = 1;
   else
      cnt = 2;

   for (; cnt; cnt--)
   {
      sptr = NameValue;
      if (cnt == 2)
      {
         /* server certificate extensions */
         SslPtr = sesolaptr->SslPtr;
         CertPtr = SSL_get_certificate (SslPtr);
         for (cptr = "SSL_SERVER_E_"; *cptr; *sptr++ = *cptr++);
      }
      else
      {
         /* client certificate extensions */
         if (!(CertPtr = sesolaptr->ClientCertPtr)) break;
         for (cptr = "SSL_CLIENT_E_"; *cptr; *sptr++ = *cptr++);
      }

      if (rqptr->rqPathSet.SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_OID)
         SesolaCertExtension (CertPtr, "OID");
      else
         SesolaCertExtension (CertPtr, NULL);

      while (cptr = SesolaCertExtension (NULL, NULL))
      {
         zptr = (sptr = NameValue+13) + sizeof(NameValue)-14;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (VMSnok (status = CgiVariable (rqptr, NameValue, NULL, VarType)))
            return (status);
      }

      for (idx = 0; ShortHand[idx]; idx += 2)
      {
         if (cptr = SesolaCertExtension (NULL, ShortHand[idx]))
         {
            while (*cptr && *cptr != '=') cptr++;
            zptr = (sptr = NameValue+13) + sizeof(NameValue)-14;
            for (aptr = ShortHand[idx+1];
                 *aptr && sptr < zptr;
                 *sptr++ = *aptr++);
            while (*cptr && sptr < zptr) *sptr++ = *cptr++;
            *sptr = '\0';
            if (VMSnok (status = CgiVariable (rqptr, NameValue, NULL, VarType)))
               return (status);
         }
      }
   }

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
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 WATCH_STRUCT  Watch;

SesolaCgiGenerateVariables
(
REQUEST_STRUCT *rqptr,
int VarType
)
{
   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_SESOLA))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_SESOLA,
                 "SesolaCgiGenerateVariables()");

   if (VMSnok (status =
       CgiVariable (rqptr, "SECURITY_STATUS", "NONE", VarType)))
      return (status);

   return (SS$_NORMAL);
}

/************************/
#endif  /* ifdef SESOLA */
/************************/

/*****************************************************************************/

