/*
.------------------------------------------------------------------------.
|                                                                        |
|  Funzione    : SSLCreateContext                                        |
|                                                                        |
|  Descrizione : Crea un nuovo Contesto SSL                              |
|                                                                        |
|  Return-Code :  0   Ok                                                 |
|                -1   Inizializzazione SSL non riuscita.                 |
|                -2   Versione SSL/TLS non valida.                       |
|                -3   Impostazione CipherSuite non riuscita.             |
|                -4   Certificato non valido o formato non supportato.   |
|                -5   Chiave Privata non valida o decrypt fallito        |
|                -6   Truststore non valido o formato non supportato.    |
|                                                                        |
|                                                                        |
|                                                                        |
|                                                                        |
|                                                                        |
|                                                                        |
|                                                                        |
'------------------------------------------------------------------------'*/
extern struct tSSLCreateContext 
SSLCreateContext ( char *keystore, char *password, char *truststore, char *ciphersuite, char *sslversion, int debug_level )
{
    SSL_METHOD *meth;

    int rSSL_library_init; 

    BIO *bio_err     ;
    
    /*
    .------------------------------------------.
    | Dichiarazione delle STRUTTURE            |
    '------------------------------------------' */
    struct tSSLCreateContext    rSSLCreateContext;
    struct tSSLCreateContext   *pSSLCreateContext;

    /*
    .------------------------------------.
    | Allocazione Spazio                 |
    '------------------------------------' */
    pSSLCreateContext     = malloc(sizeof(struct tSSLCreateContext));
    meth                  = malloc(1024); 
    rSSLCreateContext.ctx = malloc(1024); 

    /*
    .------------------------------------.
    | Utilizzo dello spazio allocato     |
    '------------------------------------' */
    pSSLCreateContext = &rSSLCreateContext;

    /*
    .-----------------------------.
    | Inizializzazione Strutture  |
    '-----------------------------' */
    memset((void*)&rSSLCreateContext,0,sizeof(rSSLCreateContext));
    rSSLCreateContext.return_code = 0; 

    /*
    .------------------------------------------------------------------------.
    | PASSO 1. Inizializzazione SSL.                                         |
    +------------------------------------------------------------------------+
    | Operazione eseguita con successo se la funzione ritorna il valore 1.   | 
    | Non ho capito bene a cosa serve questa funzione, forse alloca spazio   |
    | oppure verifica la presenza delle api di openssl o la versione.        |
    | Da approfondire l'argomento.                                           |
    '------------------------------------------------------------------------' */
    rSSL_library_init = SSL_library_init();
    if (rSSL_library_init != 1) 
    { 
        rSSLCreateContext.return_code = -1 ;          
        return rSSLCreateContext; 
    }; 

    /*
    .-----------------------------------------------------------------------.
    | OpenSSL :: Gestione degli errori                                      |
    +-----------------------------------------------------------------------+
    | Carica le stringhe di errore. Visualizza l'errore nel dispositivo     |
    | standard di errore. Al momento questa questione degli errori non è    |
    | utilizzata in maniera tale da personalizzare l'output verso           |
    | l'utente. Argomento da approfondire.                                  |
    '-----------------------------------------------------------------------' */ 
    /*
    |
    | SSL_load_error_strings();
    |  
    | // An error write context 
    | bio_err = BIO_new_fp ( stderr, BIO_NOCLOSE );
    | if ( bio_err == NULL ) 
    | { 
    |      // ERRORE  
    | }; 
    |
    */

    /*
    .-----------------------------------------------------------------------.
    | Set up a SIGPIPE hendler                                              |
    +-----------------------------------------------------------------------+
    | Approfondire la gestione dei segnali.                                 |
    '-----------------------------------------------------------------------' */
    /*
    |
    | signal ( SIGPIPE, sigpipe_handle );
    |
    */

 
    /*
    .----------------------------------------------------------------------.
    | PASSO 2. Crea il contesto per la versione di SSL selezionata.        |
    +----------------------------------------------------------------------+
    | Le versioni si SSL supportate sono 2, 3 e TLS versione 1. Se non è   |
    | specificata viene utilizzata la versione TLS versione 1 di default.  |
    | A seconda della versione prescelta o selezionata di default è        |
    | necessario invocare una funzione differente. Tali funzioni allocano  |
    | una struttura in memoria differente per ogni versione di SSL/TLS     |
    | da utilizzare (questo è il motivo per cui esistono più funzioni)     |
    | il cui puntatore viene salvato in una variabile di appoggio          |
    | denominata meth. Tale puntatore alla struttura di contesto SSL viene |
    | passato alla funzione SSL_CTX_new che crea il contesto e ritorna il  |
    | puntatore che viene salvato nella struttura di ritorno della         |
    | funzione oltre ad essere utilizzato di seguito quando si farà        |   
    | farà riferimento al contesto stesso.                                 |  
    '----------------------------------------------------------------------' */
    if ( sslversion                     != NULL && 
         strcmp ( sslversion, "SSLv2" ) != 0    && 
         strcmp ( sslversion, "SSLv3" ) != 0    && 
         strcmp ( sslversion, "TLSv1" ) != 0 ) 
    {
         rSSLCreateContext.return_code = -2 ;
         return rSSLCreateContext;
    };

    if ( sslversion == NULL ) 
    {
         meth = SSLv23_method();
    }
    else
    { 
         if ( strcmp ( sslversion, "SSLv2" ) == 0 ) meth = SSLv2_method()  ; 
         if ( strcmp ( sslversion, "SSLv3" ) == 0 ) meth = SSLv3_method()  ; 
         if ( strcmp ( sslversion, "TLSv1" ) == 0 ) meth = SSLv23_method() ; 
    };

    rSSLCreateContext.ctx = SSL_CTX_new ( meth );


    /*
    .----------------------------------------------------------------------.
    | PASSO 3. Carica nel contesto le Ciphersuite.                         |
    +----------------------------------------------------------------------+
    | Il client può specificare solamente alcune ciphersuite disponibili   |
    | ed implementate nella versione di SSL selezionato. Se non vengono    |
    | specificate valgono tutte, cioè il client le propone tutte al        |
    | server e questo pi sceglierà quella preferita.                       |
    | La funzione che solge quetso lavoro ha bisogno chiaramente del       |
    | puntatore al contesto e dell'elenco delle ciphersuite secondo la     |
    | terminologia standard separate l'una dall'altra tramite un carattere |
    | di separazione, che è il carattere dei due punti (:).                |
    | Se la funziona trova una sola ciphersuite non valida fallisce.       |
    '----------------------------------------------------------------------' */ 
    if ( ciphersuite != NULL)  
    {
         if ( SSL_CTX_set_cipher_list ( rSSLCreateContext.ctx, ciphersuite ) <= 0) 
         {
              rSSLCreateContext.return_code = -3 ;
              return rSSLCreateContext;
         };
    };

    /*
    .---------------------------------------------------------------------.
    | PASSO 4. Carica la nostro Certificato.                              |
    +---------------------------------------------------------------------+
    | Carica il certificato nel nostro contesto tramite una funzione      |
    | specifica cui è necessario passare puntatore al contesto e          |
    | file che lo contiene nel formato PEM, unico formato al momento      |
    | dalla funzione. Se il file contiene altro o il certificato non      |
    | valido o in formato diverso l'acquisizione del certifciato          |
    | chiaramente fallisce. Si può anche non passare il certificato in    |
    | quel caso il certificato non verrà passato al server durante        |
    | l'handeshake. Nel caso di mutua autenticazione cioè se il server    |
    | si vuole accertare dell'identità del client l'handeshake poi        |
    | fallisce e la sessione ssl non ha inizio.                           |
    '---------------------------------------------------------------------' */
    if ( keystore != NULL )
    {
         if ( debug_level == __INFO__ )
              printf("The client has specified as digital identity %s\n", keystore);

         /*
         .----------------------------------------------.
         | Verifica la presenza del keystore file       |
         '----------------------------------------------' */
         FILE *fp ;
         fp = fopen(keystore, "r");
         if (!fp)
         {
              rSSLCreateContext.return_code = -4 ;
              return rSSLCreateContext;
         };
         fclose(fp);

         /*
         .----------------------------------------------.
         | Prova a caricare nel contesto il certificato |
         '----------------------------------------------' */
         if(!(SSL_CTX_use_certificate_chain_file(rSSLCreateContext.ctx, keystore))) 
         { 
            rSSLCreateContext.return_code = -4 ; 
            return rSSLCreateContext; 
         } 
    };

    if ( keystore == NULL )
         if ( debug_level == __INFO__ )
              printf("The client did not specify its digital identity. No mutual authentication *Warning*\n");


    /*
    .---------------------------------------------------------------------.
    | PASSO 5. Password per il decrypt della chiave privata.              |
    +---------------------------------------------------------------------+ 
    | Specifica la password da utilizzare per il decrypt della chiave     |
    | privata. La funzione non prevede return code, non fa altro che      |
    | memorizzare la password nel contesto. Se la password è errata, cioè |
    | se non riesce ad effettuare il decrypt della chiave privata il      |
    | problema sarà evidente solamente durante l'acquisizione nel         |
    | contesto di quest'ultima.                                           |
    '---------------------------------------------------------------------' */
    if ( keystore != NULL && password != NULL )
    {
         SSL_CTX_set_default_passwd_cb_userdata(rSSLCreateContext.ctx, password); 
    };

    /*
    .--------------------------------------------------------------------.
    | PASSO 6. Carica la nostra Chiave Privata                           |
    +--------------------------------------------------------------------+
    | Carica la chiave privata nel contesto, questa deve essere          |
    | memorizzata in un keystore di tipo PEM, altri formati formati al   |
    | momento non sono supportati. Alla funzione viene passato il        |
    | puntatore al contesto SSL, il tipo di file impostato a PEM come    |
    | detto ed il file che contiene la chiave privata. Nel caso in cui   |
    | precedentemente è stato memorizzato la password nel contesto la    |
    | funzione cerca di utilizzarla per il decrypt della chiave privata  |
    | Se la password non viene passata nel contesto la funzione da per   |
    | scontato che la chiave privata sia stata memorizzata in chiaro.    |
    | Il caricamento può fallire per vari motivi: password errata quindi |
    | decrypt fallito o chiave privata non corrispondente al certificato | 
    '--------------------------------------------------------------------' */
    if ( keystore != NULL )
    {
         /*
         .----------------------------------------------.
         | Verifica la presenza del keystore file       |
         '----------------------------------------------' */
         FILE *fp ; 
         fp = fopen(keystore, "r");
         if (!fp)
         {
              rSSLCreateContext.return_code = -5 ;
              return rSSLCreateContext; 
         };
         fclose(fp);

         /*
         .----------------------------------------------.
         | Carica la chiave privata nel contesto        |
         '----------------------------------------------' */
         if(!(SSL_CTX_use_PrivateKey_file(rSSLCreateContext.ctx, keystore, SSL_FILETYPE_PEM))) 
         { 
              rSSLCreateContext.return_code = -5 ;
              return rSSLCreateContext; 
         } 
    }; 

    /*
    .--------------------------------------------------------------------.
    | PASSO 7. Carica le CA presenti nel nostro Truststore.              |
    +--------------------------------------------------------------------+
    | Viene caricato nel contesto il truststore, al momento è possibile  |
    | acquisire un'unica CA quindi catene di CA legate tra loro non sono |
    | supportate.                                                        | 
    '--------------------------------------------------------------------' */
    if ( truststore != NULL )
    {
         if ( debug_level == __INFO__ )
              printf("The client has specified as truststore (Certification Authority) %s\n", truststore);

         /*
         .---------------------------------------------------------.
         | PASSO 7.1. Verifica la presenza del truststore file     |
         '---------------------------------------------------------' */
         FILE *fp ;
         fp = fopen(truststore, "r");
         if (!fp)
         {
              rSSLCreateContext.return_code = -6 ;
              return rSSLCreateContext;
         };
         fclose(fp);

         /*
         .--------------------------------------------------------.
         | PASSO 7.2. Specifica la posizione dell CA              |
         +--------------------------------------------------------+ 
         | #include <openssl/ssl.h>                               | 
         |                                                        |
         | int SSL_CTX_load_verify_locations                      | 
         |        ( SSL_CTX *ctx, const char *CAfile,             | 
         |                   const char *CApath);                 | 
         |                                                        |

SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located. 
The certificates available via CAfile and CApath are trusted.

If CAfile is not NULL, it points to a file of CA certificates in PEM format. The file can contain several CA certificates identified by

 -----BEGIN CERTIFICATE-----
 ... (CA certificate in base64 encoding) ...
 -----END CERTIFICATE-----

sequences. Before, between, and after the certificates text is allowed which can be used e.g. for descriptions of the certificates.

The CAfile is processed on execution of the SSL_CTX_load_verify_locations() function.

If CApath is not NULL, it points to a directory containing CA certificates in PEM format. The files each contain one CA certificate. The files are looked up by the CA subject name hash value, which must hence be available. If more than one CA certificate with the same name hash value exist, the extension must be different (e.g. 9d66eef0.0, 9d66eef0.1 etc). The search is performed in the ordering of the extension number, regardless of other properties of the certificates. Use the c_rehash utility to create the necessary links.

The certificates in CApath are only looked up when required, e.g. when building the certificate chain or when actually performing the verification of a peer certificate.

When looking up CA certificates, the OpenSSL library will first search the certificates in CAfile, then those in CApath. Certificate matching is done based on the subject name, the key identifier (if present), and the serial number as taken from the certificate to be verified. If these data do not match, the next certificate will be tried. If a first certificate matching the parameters is found, the verification process will be performed; no other certificates for the same parameters will be searched in case of failure.

In server mode, when requesting a client certificate, the server must send the list of CAs of which it will accept client certificates. This list is not influenced by the contents of CAfile or CApath and must explicitly be set using the SSL_CTX_set_client_CA_list(3) family of functions.

When building its own certificate chain, an OpenSSL client/server will try to fill in missing certificates from CAfile/CApath, if the certificate chain was not explicitly specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3).

WARNINGS

If several CA certificates matching the name, key identifier, and serial number condition are available, only the first one will be examined. This may lead to unexpected results if the same CA certificate is available with different expiration dates. If a ``certificate expired'' verification error occurs, no other certificate will be searched. Make sure to not have expired certificates mixed with valid ones.

EXAMPLES

Generate a CA certificate file with descriptive text from the CA certificates ca1.pem ca2.pem ca3.pem:

 #!/bin/sh
 rm CAfile.pem
 for i in ca1.pem ca2.pem ca3.pem ; do
   openssl x509 -in $i -text >> CAfile.pem
 done

Prepare the directory /some/where/certs containing several CA certificates for use as CApath:

 cd /some/where/certs
 c_rehash .

RETURN VALUES

The following return values can occur:

0    The operation failed because CAfile and CApath are NULL or the processing at one of the locations specified failed. 
     Check the error stack to find out the reason.

1    The operation succeeded.

         '--------------------------------------------------------' */
         if(!(SSL_CTX_load_verify_locations(rSSLCreateContext.ctx, truststore, 0))) 
         { 
            rSSLCreateContext.return_code = -6 ;
            return rSSLCreateContext; 
         }; 

         /*
         .----------------------------------------------.
         | Carica il truststore nel contesto            |
SSL_CTX_set_client_CA_list, SSL_set_client_CA_list, SSL_CTX_add_client_CA, SSL_add_client_CA - set list of CAs sent to the client when requesting a client certificate

SYNOPSIS

 #include <openssl/ssl.h>
 
 void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *list);
 void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *list);
 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *cacert);
 int SSL_add_client_CA(SSL *ssl, X509 *cacert);

DESCRIPTION

SSL_CTX_set_client_CA_list() sets the list of CAs sent to the client when requesting a client certificate for ctx.

SSL_set_client_CA_list() sets the list of CAs sent to the client when requesting a client certificate for the chosen ssl, overriding the setting valid for ssl's SSL_CTX object.

SSL_CTX_add_client_CA() adds the CA name extracted from cacert to the list of CAs sent to the client when requesting a client certificate for ctx.

SSL_add_client_CA() adds the CA name extracted from cacert to the list of CAs sent to the client when requesting a client certificate for the chosen ssl, overriding the setting valid for ssl's SSL_CTX object.

NOTES
 Quando un TLS/SSL server richiede il certificato client (vedi SSL_CTX_set_verify_options()), esso trasmette una lista di CA, 
 per le quali esso accettarà il certificato, al client.
 
 Questa lista deve esplicitamente essere settata usando SSL_CTX_set_client_CA_list() per ctx e SSL_set_client_CA_list() per lo specifico ssl.
 La lista specificata 

This list must explicitly be set using SSL_CTX_set_client_CA_list() for ctx and SSL_set_client_CA_list() for the specific ssl. The list specified overrides the previous setting. The CAs listed do not become trusted (list only contains the names, not the complete certificates); use SSL_CTX_load_verify_locations(3) to additionally load them for verification.

If the list of acceptable CAs is compiled in a file, the SSL_load_client_CA_file(3) function can be used to help importing the necessary data.

SSL_CTX_add_client_CA() and SSL_add_client_CA() can be used to add additional items the list of client CAs. If no list was specified before using SSL_CTX_set_client_CA_list() or SSL_set_client_CA_list(), a new client CA list for ctx or ssl (as appropriate) is opened.

These functions are only useful for TLS/SSL servers.

RETURN-CODE 
-----------

SSL_CTX_set_client_CA_list() e SSL_set_client_CA_list() non ritornano informazioni diagnostiche.
SSL_CTX_add_client_CA() e SSL_add_client_CA() prevedono i seguenti codici di ritorno :

1 The operation succeeded.
0 A failure while manipulating the STACK_OF(X509_NAME) object occurred or the X509_NAME could not be extracted from cacert. 
  Check the error stack to find out the reason.

ESEMPI 
------

Scan all certificates in CAfile and list them as acceptable CAs:

SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile));

=======================================================

SSL_load_client_CA_file - load certificate names from file

SYNOPSIS

 #include <openssl/ssl.h>

 STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file);

DESCRIPTION

SSL_load_client_CA_file() reads certificates from file and returns a STACK_OF(X509_NAME) with the subject names found.

NOTES

SSL_load_client_CA_file() reads a file of PEM formatted certificates and extracts the X509_NAMES of the certificates found. While the name suggests the specific usage as support function for SSL_CTX_set_client_CA_list(3), it is not limited to CA certificates.

EXAMPLES

Load names of CAs from file and use it as a client CA list:

 SSL_CTX *ctx;
 STACK_OF(X509_NAME) *cert_names;

 ...
 cert_names = SSL_load_client_CA_file("/path/to/CAfile.pem");
 if (cert_names != NULL)
   SSL_CTX_set_client_CA_list(ctx, cert_names);
 else
   error_handling();
 ...

RETURN VALUES

The following return values can occur:

NULL

    The operation failed, check out the error stack for the reason.
Pointer to STACK_OF(X509_NAME)

    Pointer to the subject names of the successfully read certificates.


         '----------------------------------------------' */
         SSL_CTX_set_client_CA_list(rSSLCreateContext.ctx, SSL_load_client_CA_file(truststore)); 

         /*
         .------------------------------------------------------.
         | Visualizza il numero delle CA caricate nel contesto  |
         '------------------------------------------------------' */
         STACK_OF(X509_NAME) *ca_list ; 
         ca_list = (STACK_OF(X509_NAME) *) SSL_CTX_get_client_CA_list(rSSLCreateContext.ctx); 
         printf("+Numero CA caricate nel contesto: [%d]\n", sk_X509_NAME_num(ca_list)); 

    };

    if ( truststore == NULL )
         if ( debug_level == __INFO__ )
              printf("The client did not specify any truststore (Certification Authority) *Warning*\n");

    return rSSLCreateContext;
}

