[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
[0735]
[0736]
[0737]
[0738]
[0739]
[0740]
[0741]
[0742]
[0743]
[0744]
[0745]
[0746]
[0747]
[0748]
[0749]
[0750]
[0751]
[0752]
[0753]
[0754]
[0755]
[0756]
[0757]
[0758]
[0759]
[0760]
[0761]
[0762]
[0763]
[0764]
[0765]
[0766]
[0767]
[0768]
[0769]
[0770]
[0771]
[0772]
[0773]
[0774]
[0775]
[0776]
[0777]
[0778]
[0779]
[0780]
[0781]
[0782]
[0783]
[0784]
[0785]
[0786]
[0787]
[0788]
[0789]
[0790]
[0791]
[0792]
[0793]
[0794]
[0795]
[0796]
[0797]
[0798]
[0799]
[0800]
[0801]
[0802]
[0803]
[0804]
[0805]
[0806]
[0807]
[0808]
[0809]
[0810]
[0811]
/*****************************************************************************/
/*
                                 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 */
/************************/

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