Add support for printing out and retrieving EC point formats extension.
[openssl.git] / apps / s_cb.c
index b21a4283dfb478499fd9adc88810d296e5730edb..2ac7656f063fb478c26bbc335b7c2bd06937a480 100644 (file)
@@ -237,8 +237,8 @@ int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file)
 
                /* If we are using DSA, we can copy the parameters from
                 * the private key */
-               
-               
+
+
                /* Now we know that a key and cert have been set against
                 * the SSL context */
                if (!SSL_CTX_check_private_key(ctx))
@@ -251,9 +251,9 @@ int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file)
        }
 
 int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
-                                                       STACK_OF(X509) *chain)
+                      STACK_OF(X509) *chain, int build_chain)
        {
-       if (cert ==  NULL)
+       if (cert == NULL)
                return 1;
        if (SSL_CTX_use_certificate(ctx,cert) <= 0)
                {
@@ -261,16 +261,16 @@ int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
                ERR_print_errors(bio_err);
                return 0;
                }
-       if (SSL_CTX_use_PrivateKey(ctx,key) <= 0)
-               {
-               BIO_printf(bio_err,"error setting private key\n");
-               ERR_print_errors(bio_err);
-               return 0;
-               }
 
-               
-               /* Now we know that a key and cert have been set against
-                * the SSL context */
+       if (SSL_CTX_use_PrivateKey(ctx,key) <= 0)
+               {
+               BIO_printf(bio_err,"error setting private key\n");
+               ERR_print_errors(bio_err);
+               return 0;
+               }
+                
+       /* Now we know that a key and cert have been set against
+        * the SSL context */
        if (!SSL_CTX_check_private_key(ctx))
                {
                BIO_printf(bio_err,"Private key does not match the certificate public key\n");
@@ -282,23 +282,109 @@ int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
                ERR_print_errors(bio_err);
                return 0;
                }
+       if (!chain && build_chain && !SSL_CTX_build_cert_chain(ctx, 0))
+               {
+               BIO_printf(bio_err,"error building certificate chain\n");
+               ERR_print_errors(bio_err);
+               return 0;
+               }
+               
        return 1;
        }
 
-int ssl_print_sigalgs(BIO *out, SSL *s)
+static void ssl_print_client_cert_types(BIO *bio, SSL *s)
+       {
+       const unsigned char *p;
+       int i;
+       int cert_type_num = SSL_get0_certificate_types(s, &p);
+       if (!cert_type_num)
+               return;
+       BIO_puts(bio, "Client Certificate Types: ");
+       for (i = 0; i < cert_type_num; i++)
+               {
+               unsigned char cert_type = p[i];
+               char *cname;
+               switch(cert_type)
+                       {
+               case TLS_CT_RSA_SIGN:
+                       cname = "RSA sign";
+                       break;
+
+               case TLS_CT_DSS_SIGN:
+                       cname = "DSA sign";
+                       break;
+
+               case TLS_CT_RSA_FIXED_DH:
+                       cname = "RSA fixed DH";
+                       break;
+
+               case TLS_CT_DSS_FIXED_DH:
+                       cname = "DSS fixed DH";
+                       break;
+
+               case TLS_CT_ECDSA_SIGN:
+                       cname = "ECDSA sign";
+                       break;
+
+               case TLS_CT_RSA_FIXED_ECDH:
+                       cname = "RSA fixed ECDH";
+                       break;
+
+               case TLS_CT_ECDSA_FIXED_ECDH:
+                       cname = "ECDSA fixed ECDH";
+                       break;
+
+               case TLS_CT_GOST94_SIGN:
+                       cname = "GOST94 Sign";
+                       break;
+
+               case TLS_CT_GOST01_SIGN:
+                       cname = "GOST01 Sign";
+                       break;
+
+               default:
+                        cname = NULL;
+                       }
+
+               if (i)
+                       BIO_puts(bio, ", ");
+
+               if (cname)
+                       BIO_puts(bio, cname);
+               else
+                       BIO_printf(bio, "UNKNOWN (%d),", cert_type);
+               }
+       BIO_puts(bio, "\n");
+       }
+
+static int do_print_sigalgs(BIO *out, SSL *s, int shared)
        {
-       int i, nsig;
-       nsig = SSL_get_sigalgs(s, -1, NULL, NULL, NULL, NULL, NULL);
+       int i, nsig, client;
+       client = SSL_is_server(s) ? 0 : 1;
+       if (shared)
+               nsig = SSL_get_shared_sigalgs(s, -1, NULL, NULL, NULL,
+                                                       NULL, NULL);
+       else
+               nsig = SSL_get_sigalgs(s, -1, NULL, NULL, NULL, NULL, NULL);
        if (nsig == 0)
                return 1;
 
+       if (shared)
+               BIO_puts(out, "Shared ");
+
+       if (client)
+               BIO_puts(out, "Requested ");
        BIO_puts(out, "Signature Algorithms: ");
        for (i = 0; i < nsig; i++)
                {
                int hash_nid, sign_nid;
                unsigned char rhash, rsign;
                const char *sstr = NULL;
-               SSL_get_sigalgs(s, i, &sign_nid, &hash_nid, NULL,
+               if (shared)
+                       SSL_get_shared_sigalgs(s, i, &sign_nid, &hash_nid, NULL,
+                                                       &rsign, &rhash);
+               else
+                       SSL_get_sigalgs(s, i, &sign_nid, &hash_nid, NULL,
                                                        &rsign, &rhash);
                if (i)
                        BIO_puts(out, ":");
@@ -321,6 +407,57 @@ int ssl_print_sigalgs(BIO *out, SSL *s)
        return 1;
        }
 
+int ssl_print_sigalgs(BIO *out, SSL *s)
+       {
+       int mdnid;
+       if (!SSL_is_server(s))
+               ssl_print_client_cert_types(out, s);
+       do_print_sigalgs(out, s, 0);
+       do_print_sigalgs(out, s, 1);
+       if (SSL_get_peer_signature_nid(s, &mdnid))
+               BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(mdnid));
+       return 1;
+       }
+int ssl_print_point_formats(BIO *out, SSL *s)
+       {
+       int i, nformats;
+       const char *pformats;
+       nformats = SSL_get0_ec_point_formats(s, &pformats);
+       if (nformats <= 0)
+               return 1;
+       BIO_puts(out, "Supported Elliptic Curve Point Formats: ");
+       for (i = 0; i < nformats; i++, pformats++)
+               {
+               if (i)
+                       BIO_puts(out, ":");
+               switch(*pformats)
+                       {
+               case TLSEXT_ECPOINTFORMAT_uncompressed:
+                       BIO_puts(out, "uncompressed");
+                       break;
+
+               case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime:
+                       BIO_puts(out, "ansiX962_compressed_prime");
+                       break;
+
+               case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2:
+                       BIO_puts(out, "ansiX962_compressed_char2");
+                       break;
+
+               default:
+                       BIO_printf(out, "unknown(%d)", (int)*pformats);
+                       break;
+
+                       }
+               }
+       if (nformats <= 0)
+               BIO_puts(out, "NONE");
+       BIO_puts(out, "\n");
+       return 1;
+       }
+
+
 int ssl_print_curves(BIO *out, SSL *s)
        {
        int i, ncurves, *curves, nid;
@@ -368,6 +505,40 @@ int ssl_print_curves(BIO *out, SSL *s)
        return 1;
        }
 
+int ssl_print_tmp_key(BIO *out, SSL *s)
+       {
+       EVP_PKEY *key;
+       if (!SSL_get_server_tmp_key(s, &key))
+               return 1;
+       BIO_puts(out, "Server Temp Key: ");
+       switch (EVP_PKEY_id(key))
+               {
+       case EVP_PKEY_RSA:
+               BIO_printf(out, "RSA, %d bits\n", EVP_PKEY_bits(key));
+               break;
+
+       case EVP_PKEY_DH:
+               BIO_printf(out, "DH, %d bits\n", EVP_PKEY_bits(key));
+               break;
+
+       case EVP_PKEY_EC:
+                       {
+                       EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key);
+                       int nid;
+                       const char *cname;
+                       nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+                       EC_KEY_free(ec);
+                       cname = EC_curve_nid2nist(nid);
+                       if (!cname)
+                               cname = OBJ_nid2sn(nid);
+                       BIO_printf(out, "ECDH, %s, %d bits\n",
+                                               cname, EVP_PKEY_bits(key));
+                       }
+               }
+       EVP_PKEY_free(key);
+       return 1;
+       }
+               
 
 long MS_CALLBACK bio_dump_callback(BIO *bio, int cmd, const char *argp,
                                   int argi, long argl, long ret)
@@ -527,6 +698,8 @@ void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *
 
        if (version == SSL3_VERSION ||
            version == TLS1_VERSION ||
+           version == TLS1_1_VERSION ||
+           version == TLS1_2_VERSION ||
            version == DTLS1_VERSION ||
            version == DTLS1_BAD_VER)
                {
@@ -1017,3 +1190,311 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
 
        return 0;
        }
+
+/* Example of extended certificate handling. Where the standard support
+ * of one certificate per algorithm is not sufficient an application
+ * can decide which certificate(s) to use at runtime based on whatever
+ * criteria it deems appropriate.
+ */
+
+/* Linked list of certificates, keys and chains */
+struct  ssl_excert_st
+       {
+       int certform;
+       const char *certfile;
+       int keyform;
+       const char *keyfile;
+       const char *chainfile;
+       X509 *cert;
+       EVP_PKEY *key;
+       STACK_OF(X509) *chain;
+       int build_chain;
+       struct ssl_excert_st *next, *prev;
+       };
+
+struct chain_flags
+       {
+       int flag;
+       const char *name;
+       };
+
+struct chain_flags chain_flags_list[] =
+       {
+               {CERT_PKEY_VALID, "Overall Validity"},
+               {CERT_PKEY_SIGN,  "Sign with EE key"},
+               {CERT_PKEY_EE_SIGNATURE, "EE signature"},
+               {CERT_PKEY_CA_SIGNATURE, "CA signature"},
+               {CERT_PKEY_EE_PARAM, "EE key parameters"},
+               {CERT_PKEY_CA_PARAM, "CA key parameters"},
+               {CERT_PKEY_EXPLICIT_SIGN,  "Explicity sign with EE key"},
+               {CERT_PKEY_ISSUER_NAME,  "Issuer Name"},
+               {CERT_PKEY_CERT_TYPE,  "Certificate Type"},
+               {0, NULL}
+       };
+
+
+static void print_chain_flags(BIO *out, int flags)
+       {
+       struct chain_flags *ctmp = chain_flags_list;
+       while(ctmp->name)
+               {
+               BIO_printf(out, "\t%s: %s\n", ctmp->name,
+                               flags & ctmp->flag ? "OK" : "NOT OK");
+               ctmp++;
+               }
+       }
+
+/* Very basic selection callback: just use any certificate chain
+ * reported as valid. More sophisticated could prioritise according
+ * to local policy.
+ */
+static int set_cert_cb(SSL *ssl, void *arg)
+       {
+       int i, rv;
+       SSL_EXCERT *exc = arg;
+       SSL_certs_clear(ssl);
+
+       if (!exc)
+               return 1;
+
+       /* Go to end of list and traverse backwards since we prepend
+        * newer entries this retains the original order.
+        */
+       while (exc->next)
+               exc = exc->next;
+
+       i = 0;  
+
+       while(exc)
+               {
+               i++;
+               rv = SSL_check_chain(ssl, exc->cert, exc->key, exc->chain);
+               BIO_printf(bio_err, "Checking cert chain %d:\nSubject: ", i);
+               X509_NAME_print_ex(bio_err, X509_get_subject_name(exc->cert), 0,
+                                                       XN_FLAG_ONELINE);
+               BIO_puts(bio_err, "\n");
+               
+               print_chain_flags(bio_err, rv);
+               if (rv & CERT_PKEY_VALID)
+                       {
+                       SSL_use_certificate(ssl, exc->cert);
+                       SSL_use_PrivateKey(ssl, exc->key);
+                       /* NB: we wouldn't normally do this as it is
+                        * not efficient building chains on each connection
+                        * better to cache the chain in advance.
+                        */
+                       if (exc->build_chain)
+                               {
+                               if (!SSL_build_cert_chain(ssl, 0))
+                                       return 0;
+                               }
+                       else if (exc->chain)
+                               SSL_set1_chain(ssl, exc->chain);
+                       }
+               exc = exc->prev;
+               }
+       return 1;
+       }
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc)
+       {
+       SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc);
+       }
+
+static int ssl_excert_prepend(SSL_EXCERT **pexc)
+       {
+       SSL_EXCERT *exc;
+       exc = OPENSSL_malloc(sizeof(SSL_EXCERT));
+       if (!exc)
+               return 0;
+       exc->certfile = NULL;
+       exc->keyfile = NULL;
+       exc->chainfile = NULL;
+       exc->cert = NULL;
+       exc->key = NULL;
+       exc->chain = NULL;
+       exc->prev = NULL;
+       exc->build_chain = 0;
+
+       exc->next = *pexc;
+       *pexc = exc;
+                       
+       if (exc->next)
+               {
+               exc->certform = exc->next->certform;
+               exc->keyform = exc->next->keyform;
+               exc->next->prev = exc;
+               }
+       else
+               {
+               exc->certform = FORMAT_PEM;
+               exc->keyform = FORMAT_PEM;
+               }
+       return 1;
+
+       }
+
+void ssl_excert_free(SSL_EXCERT *exc)
+       {
+       SSL_EXCERT *curr;
+       while (exc)
+               {
+               if (exc->cert)
+                       X509_free(exc->cert);
+               if (exc->key)
+                       EVP_PKEY_free(exc->key);
+               if (exc->chain)
+                       sk_X509_pop_free(exc->chain, X509_free);
+               curr = exc;
+               exc = exc->next;
+               OPENSSL_free(curr);
+               }
+       }
+
+int load_excert(SSL_EXCERT **pexc, BIO *err)
+       {
+       SSL_EXCERT *exc = *pexc;
+       if (!exc)
+               return 1;
+       /* If nothing in list, free and set to NULL */
+       if (!exc->certfile && !exc->next)
+               {
+               ssl_excert_free(exc);
+               *pexc = NULL;
+               return 1;
+               }
+       for(; exc; exc=exc->next)
+               {
+               if (!exc->certfile)
+                       {
+                       BIO_printf(err, "Missing filename\n");
+                       return 0;
+                       }
+               exc->cert = load_cert(err, exc->certfile, exc->certform,
+                                       NULL, NULL, "Server Certificate");
+               if (!exc->cert)
+                       return 0;
+               if (exc->keyfile)
+                       exc->keyfile = exc->certfile;
+               exc->key = load_key(err, exc->certfile, exc->certform, 0,
+                                       NULL, NULL, "Server Certificate");
+               if (!exc->key)
+                       return 0;
+               if (exc->chainfile)
+                       {
+                       exc->chain = load_certs(err,
+                                               exc->chainfile, FORMAT_PEM,
+                                               NULL, NULL,
+                                               "Server Chain");
+                       if (!exc->chainfile)
+                               return 0;
+                       }
+               }
+       return 1;
+       }
+               
+
+int args_excert(char ***pargs, int *pargc,
+                       int *badarg, BIO *err, SSL_EXCERT **pexc)
+       {
+       char *arg = **pargs, *argn = (*pargs)[1];
+       SSL_EXCERT *exc = *pexc;
+       int narg = 2;
+       if (!exc)
+               {
+               if (ssl_excert_prepend(&exc))
+                       *pexc = exc;
+               else
+                       {
+                       BIO_printf(err, "Error initialising xcert\n");
+                       *badarg = 1;
+                       goto err;
+                       }
+               }
+       if (strcmp(arg, "-xcert") == 0)
+               {
+               if (!argn)
+                       {
+                       *badarg = 1;
+                       return 1;
+                       }
+               if (exc->certfile && !ssl_excert_prepend(&exc))
+                       {
+                       BIO_printf(err, "Error adding xcert\n");
+                       *badarg = 1;
+                       goto err;
+                       }
+               exc->certfile = argn;
+               }
+       else if (strcmp(arg,"-xkey") == 0)
+               {
+               if (!argn)
+                       {
+                       *badarg = 1;
+                       return 1;
+                       }
+               if (exc->keyfile)
+                       {
+                       BIO_printf(err, "Key already specified\n");
+                       *badarg = 1;
+                       return 1;
+                       }
+               exc->keyfile = argn;
+               }
+       else if (strcmp(arg,"-xchain") == 0)
+               {
+               if (!argn)
+                       {
+                       *badarg = 1;
+                       return 1;
+                       }
+               if (exc->chainfile)
+                       {
+                       BIO_printf(err, "Chain already specified\n");
+                       *badarg = 1;
+                       return 1;
+                       }
+               exc->chainfile = argn;
+               }
+       else if (strcmp(arg,"-xchain_build") == 0)
+               {
+               narg = 1;
+               exc->build_chain = 1;
+               }
+       else if (strcmp(arg,"-xcertform") == 0)
+               {
+               if (!argn)
+                       {
+                       *badarg = 1;
+                       goto err;
+                       }
+               exc->certform = str2fmt(argn);
+               }
+       else if (strcmp(arg,"-xkeyform") == 0)
+               {
+               if (!argn)
+                       {
+                       *badarg = 1;
+                       goto err;
+                       }
+               exc->keyform = str2fmt(argn);
+               }
+       else
+               return 0;
+
+       (*pargs) += narg;
+
+       if (pargc)
+               *pargc -= narg;
+
+       *pexc = exc;
+
+       return 1;
+
+       err:
+       ERR_print_errors(err);
+       ssl_excert_free(exc);
+       *pexc = NULL;
+       return 1;
+       }
+