diff options
author | Simon Josefsson <simon@josefsson.org> | 2007-08-10 15:20:40 +0200 |
---|---|---|
committer | Simon Josefsson <simon@josefsson.org> | 2007-08-10 15:20:40 +0200 |
commit | 4431c4369db575dc8ecd8ec3622bc2dfc9bee725 (patch) | |
tree | 00edcc793ad683e774bf21e2f3befe1ab6c5e16e | |
parent | 12aaffb7aaa04b48a988b68449c64cc3c2b2d0d3 (diff) | |
download | gnutls-4431c4369db575dc8ecd8ec3622bc2dfc9bee725.tar.gz |
External signing callback interface.
* includes/gnutls/gnutls.h.in (gnutls_sign_func): New type.
(gnutls_sign_callback_set): New function.
* includes/gnutls/x509.h (gnutls_x509_privkey_sign_hash): New
function.
* lib/gnutls_x509.c (gnutls_certificate_set_x509_key_mem): Handle
NULL key. Doc fix.
* lib/gnutls_sig.c (_gnutls_tls_sign_hdata): Pass session to
_gnutls_tls_sign.
(_gnutls_tls_sign_params): Likewise.
(_gnutls_tls_sign): Add new parameter 'session'. Call sign
callback if appropriate.
(gnutls_sign_callback_set): New function.
* lib/gnutls_x509.c (read_key_mem): Support a NULL key.
* lib/gnutls_int.h (internals_st): Add sign_func,
sign_func_userdata.
* lib/auth_dhe.c (gen_dhe_server_kx): Use length of certificate
list to decide wheter to sign, not presence of private key.
* lib/auth_cert.c (_gnutls_gen_cert_client_cert_vrfy): Likewise.
* lib/auth_rsa_export.c (gen_rsa_export_server_kx): Likewise.
* lib/auth_cert.c(_gnutls_get_selected_cert): Don't require that
private key is present.
* lib/auth_rsa_export.c (gen_rsa_export_server_kx): Don't check
key size when key is not present, assume it is > 512 bits.
* lib/x509/privkey.c (gnutls_x509_privkey_sign_hash): New
function.
* tests/Makefile.am: Add x509signself.
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | includes/gnutls/gnutls.h.in | 13 | ||||
-rw-r--r-- | includes/gnutls/x509.h | 4 | ||||
-rw-r--r-- | lib/auth_cert.c | 5 | ||||
-rw-r--r-- | lib/auth_dhe.c | 2 | ||||
-rw-r--r-- | lib/auth_rsa_export.c | 4 | ||||
-rw-r--r-- | lib/gnutls_int.h | 6 | ||||
-rw-r--r-- | lib/gnutls_sig.c | 56 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 37 | ||||
-rw-r--r-- | lib/x509/privkey.c | 38 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/x509signself.c | 527 |
12 files changed, 673 insertions, 33 deletions
@@ -5,8 +5,14 @@ See the end for copying conditions. * Version 1.7.17 (unreleased) +** New functions to perform external signing. +Set the signing callback function (of the gnutls_sign_func prototype) +using gnutls_sign_callback_set. In the callback, you may find the new +function gnutls_x509_privkey_sign_hash useful. + ** New self test of client and server authenticated X.509 TLS sessions. -See tests/x509self.c. +See tests/x509self.c and tests/x509signself.c. The latter also tests +the new external signing callback interface. ** gnutls_set_default_priority now disable TLS 1.2 by default. The RFC is not released yet, and we're approaching a major release so @@ -24,7 +30,9 @@ Thanks to Jakub Bogusz <qboosh@pld-linux.org> and Daniel Nylander <po@danielnylander.se>. ** API and ABI modifications: -No changes since last version. +gnutls_sign_func: ADD, new type for sign callback. +gnutls_sign_callback_set: ADD, new function to set sign callback. +gnutls_x509_privkey_sign_hash: ADD, new function useful in sign callback. * Version 1.7.16 (released 2007-08-07) diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in index 4071a10855..8f1e36440d 100644 --- a/includes/gnutls/gnutls.h.in +++ b/includes/gnutls/gnutls.h.in @@ -703,7 +703,6 @@ extern "C" gnutls_x509_crl_t * crl_list, int crl_list_size); - /* global state functions */ int gnutls_global_init (void); @@ -1048,6 +1047,18 @@ extern "C" /* X509PKI */ + /* External signing callback. Experimental. */ + typedef int (*gnutls_sign_func) (gnutls_session_t session, + void *userdata, + gnutls_certificate_type_t cert_type, + gnutls_datum_t cert, + const gnutls_datum_t hash, + gnutls_datum_t * signature); + + void gnutls_sign_callback_set (gnutls_session_t session, + gnutls_sign_func sign_func, + void *userdata); + /* These are set on the credentials structure. */ void gnutls_certificate_client_set_retrieve_function diff --git a/includes/gnutls/x509.h b/includes/gnutls/x509.h index 8f570df880..8357dc28a6 100644 --- a/includes/gnutls/x509.h +++ b/includes/gnutls/x509.h @@ -605,6 +605,10 @@ extern "C" const gnutls_datum_t * data, const gnutls_datum_t * signature); + int gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key, + const gnutls_datum_t hash, + gnutls_datum_t *signature); + /* Certificate request stuff. */ diff --git a/lib/auth_cert.c b/lib/auth_cert.c index f0cb427205..54b4a50d13 100644 --- a/lib/auth_cert.c +++ b/lib/auth_cert.c @@ -1307,7 +1307,7 @@ _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data) return ret; } - if (apr_pkey != NULL) + if (apr_cert_list_length > 0) { if ((ret = _gnutls_tls_sign_hdata (session, @@ -1483,8 +1483,7 @@ _gnutls_get_selected_cert (gnutls_session_t session, *apr_pkey = session->internals.selected_key; *apr_cert_list_length = session->internals.selected_cert_list_length; - if (*apr_cert_list_length == 0 || *apr_pkey == NULL || - *apr_cert_list == NULL) + if (*apr_cert_list_length == 0 || *apr_cert_list == NULL) { gnutls_assert (); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; diff --git a/lib/auth_dhe.c b/lib/auth_dhe.c index c9ff9f5200..6cf658a242 100644 --- a/lib/auth_dhe.c +++ b/lib/auth_dhe.c @@ -145,7 +145,7 @@ gen_dhe_server_kx (gnutls_session_t session, opaque ** data) ddata.data = *data; ddata.size = data_size; - if (apr_pkey != NULL) + if (apr_cert_list_length > 0) { if ((ret = _gnutls_tls_sign_params (session, &apr_cert_list[0], diff --git a/lib/auth_rsa_export.c b/lib/auth_rsa_export.c index a16ed17f3f..a1b867a94a 100644 --- a/lib/auth_rsa_export.c +++ b/lib/auth_rsa_export.c @@ -100,7 +100,7 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) /* abort sending this message if we have a certificate * of 512 bits or less. */ - if (_gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512) + if (apr_pkey && _gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512) { gnutls_assert (); return GNUTLS_E_INT_RET_0; @@ -153,7 +153,7 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) ddata.data = *data; ddata.size = data_size; - if (apr_pkey != NULL) + if (apr_cert_list_length > 0) { if ((ret = _gnutls_tls_sign_params (session, &apr_cert_list[0], diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index b9a9dcddd1..6f01c96afb 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -627,6 +627,12 @@ typedef struct */ int errnum; + /* Function used to perform public-key signing operation during + handshake. Used by gnutls_sig.c:_gnutls_tls_sign(), see also + gnutls_sign_callback_set(). */ + gnutls_sign_func sign_func; + void *sign_func_userdata; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c index 31c845cffc..31db1cf425 100644 --- a/lib/gnutls_sig.c +++ b/lib/gnutls_sig.c @@ -37,10 +37,11 @@ #include <gnutls_sig.h> #include <gnutls_kx.h> -static - int _gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey, - const gnutls_datum_t * hash_concat, - gnutls_datum_t * signature); +static int +_gnutls_tls_sign (gnutls_session_t session, + gnutls_cert * cert, gnutls_privkey * pkey, + const gnutls_datum_t * hash_concat, + gnutls_datum_t * signature); /* Generates a signature of all the previous sent packets in the @@ -111,7 +112,7 @@ _gnutls_tls_sign_hdata (gnutls_session_t session, gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } - ret = _gnutls_tls_sign (cert, pkey, &dconcat, signature); + ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature); if (ret < 0) { gnutls_assert (); @@ -202,7 +203,7 @@ _gnutls_tls_sign_params (gnutls_session_t session, gnutls_cert * cert, _gnutls_hash_deinit (td_sha, NULL); return GNUTLS_E_INTERNAL_ERROR; } - ret = _gnutls_tls_sign (cert, pkey, &dconcat, signature); + ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature); if (ret < 0) { gnutls_assert (); @@ -257,7 +258,8 @@ _gnutls_sign (gnutls_pk_algorithm_t algo, mpi_t * params, * it supports signing. */ static int -_gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey, +_gnutls_tls_sign (gnutls_session_t session, + gnutls_cert * cert, gnutls_privkey * pkey, const gnutls_datum_t * hash_concat, gnutls_datum_t * signature) { @@ -273,11 +275,49 @@ _gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey, return GNUTLS_E_KEY_USAGE_VIOLATION; } + /* External signing. */ + if (!pkey || pkey->params_size == 0) + { + if (!session->internals.sign_func) + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + + return (*session->internals.sign_func) + (session, session->internals.sign_func_userdata, + cert->cert_type, cert->raw, + *hash_concat, signature); + } + return _gnutls_sign (pkey->pk_algorithm, pkey->params, pkey->params_size, hash_concat, signature); - } +/** + * gnutls_sign_callback_set: + * @session: + * @sign_func: + * @userdata: + * + * Set the callback function. The function must have this prototype: + * + * typedef int (*gnutls_sign_func) (gnutls_session_t session, + * void *userdata, + * gnutls_certificate_type_t cert_type, + * gnutls_datum_t cert, + * const gnutls_datum_t hash, + * gnutls_datum_t * signature); + * + * The @userdata parameter is passed to the @sign_func verbatim, and + * can be used to store application-specific data needed in the + * callback function. + **/ +void +gnutls_sign_callback_set (gnutls_session_t session, + gnutls_sign_func sign_func, + void *userdata) +{ + session->internals.sign_func = sign_func; + session->internals.sign_func_userdata = userdata; +} static int _gnutls_verify_sig (gnutls_cert * cert, diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index d4b66ac500..352f0028d0 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -702,9 +702,9 @@ _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, return 0; } -/* Reads a PEM encoded PKCS-1 RSA private key from memory - * 2002-01-26: Added ability to read DSA keys. - * type indicates the certificate format. +/* Reads a PEM encoded PKCS-1 RSA/DSA private key from memory. Type + * indicates the certificate format. KEY can be NULL, to indicate + * that GnuTLS doesn't know the private key. */ static int read_key_mem (gnutls_certificate_credentials_t res, @@ -724,16 +724,21 @@ read_key_mem (gnutls_certificate_credentials_t res, return GNUTLS_E_MEMORY_ERROR; } - tmp.data = (opaque *) key; - tmp.size = key_size; - - ret = - _gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, type); - if (ret < 0) + if (key) { - gnutls_assert (); - return ret; + tmp.data = (opaque *) key; + tmp.size = key_size; + + ret = + _gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, type); + if (ret < 0) + { + gnutls_assert (); + return ret; + } } + else + memset (&res->pkey[res->ncerts], 0, sizeof (gnutls_privkey)); return 0; } @@ -790,7 +795,7 @@ read_key_file (gnutls_certificate_credentials_t res, * gnutls_certificate_set_x509_key_mem - Used to set keys in a gnutls_certificate_credentials_t structure * @res: is an #gnutls_certificate_credentials_t structure. * @cert: contains a certificate list (path) for the specified private key - * @key: is the private key + * @key: is the private key, or %NULL * @type: is PEM or DER * * This function sets a certificate/private key pair in the @@ -811,6 +816,9 @@ read_key_file (gnutls_certificate_credentials_t res, * If the certificate and the private key are given in PEM encoding * then the strings that hold their values must be null terminated. * + * The @key may be %NULL if you are using a sign callback, see + * gnutls_sign_callback_set(). + * **/ int gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t @@ -822,7 +830,8 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t /* this should be first */ - if ((ret = read_key_mem (res, key->data, key->size, type)) < 0) + if ((ret = read_key_mem (res, key ? key->data : NULL, + key ? key->size : 0, type)) < 0) return ret; if ((ret = read_cert_mem (res, cert->data, cert->size, type)) < 0) @@ -830,7 +839,7 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t res->ncerts++; - if ((ret = _gnutls_check_key_cert_match (res)) < 0) + if (key && (ret = _gnutls_check_key_cert_match (res)) < 0) { gnutls_assert (); return ret; diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index dd29e89d4d..504482892c 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation * * Author: Nikos Mavroyanopoulos * @@ -27,6 +27,7 @@ #include <gnutls_global.h> #include <gnutls_errors.h> #include <gnutls_rsa_export.h> +#include <gnutls_sig.h> #include <common.h> #include <gnutls_x509.h> #include <x509_b64.h> @@ -1555,6 +1556,41 @@ gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, } /** + * gnutls_x509_privkey_sign_hash - This function will sign the given data using the private key params + * @key: Holds the key + * @hash: holds the data to be signed + * @signature: will contain newly allocated signature + * + * This function will sign the given hash using the private key. + * + * Return value: In case of failure a negative value will be returned, + * and 0 on success. + **/ +int +gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key, + const gnutls_datum_t hash, + gnutls_datum_t *signature) +{ + int result; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_sign (key->pk_algorithm, key->params, + key->params_size, &hash, signature); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** * gnutls_x509_privkey_verify_data - This function will verify the given signed data. * @key: Holds the key * @flags: should be 0 for now diff --git a/tests/Makefile.am b/tests/Makefile.am index 15c63e5512..209f343d71 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,7 +40,7 @@ ctests = simple openssl gc set_pkcs12_cred certder \ certificate_set_x509_crl dn parse_ca openssl_LDADD = $(LDADD) ../libextra/libgnutls-openssl.la if HAVE_FORK -ctests += x509self anonself pskself dhepskself tlsia resume +ctests += x509self x509signself anonself pskself dhepskself tlsia resume tlsia_LDADD = ../libextra/libgnutls-extra.la $(LDADD) @LTLIBREADLINE@ endif gc_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) diff --git a/tests/x509signself.c b/tests/x509signself.c new file mode 100644 index 0000000000..2a0a44c591 --- /dev/null +++ b/tests/x509signself.c @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * GNUTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GNUTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNUTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Parts copied from GnuTLS example programs. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "utils.h" + +pid_t child; + +static void +tls_log_func (int level, const char *str) +{ + fprintf (stderr, "%s |<%d>| %s", child ? "server" : "client", level, str); +} + +/* A very basic TLS client, with anonymous authentication. + */ + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +/* Connects to the peer and returns a socket + * descriptor. + */ +int +tcp_connect (void) +{ + const char *PORT = "5556"; + const char *SERVER = "127.0.0.1"; + int err, sd; + struct sockaddr_in sa; + + /* connects to server + */ + sd = socket (AF_INET, SOCK_STREAM, 0); + + memset (&sa, '\0', sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (atoi (PORT)); + inet_pton (AF_INET, SERVER, &sa.sin_addr); + + err = connect (sd, (struct sockaddr *) &sa, sizeof (sa)); + if (err < 0) + { + fprintf (stderr, "Connect error\n"); + exit (1); + } + + return sd; +} + +/* closes the given socket descriptor. + */ +void +tcp_close (int sd) +{ + shutdown (sd, SHUT_RDWR); /* no more receptions */ + close (sd); +} + +const char ca_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIB5zCCAVKgAwIBAgIERiYdJzALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n" + "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTExWhcNMDgwNDE3MTMyOTExWjAZMRcw\n" + "FQYDVQQDEw5HbnVUTFMgdGVzdCBDQTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA\n" + "vuyYeh1vfmslnuggeEKgZAVmQ5ltSdUY7H25WGSygKMUYZ0KT74v8C780qtcNt9T\n" + "7EPH/N6RvB4BprdssgcQLsthR3XKA84jbjjxNCcaGs33lvOz8A1nf8p3hD+cKfRi\n" + "kfYSW2JazLrtCC4yRCas/SPOUxu78of+3HiTfFm/oXUCAwEAAaNDMEEwDwYDVR0T\n" + "AQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBTpPBz7rZJu5gak\n" + "Viyi4cBTJ8jylTALBgkqhkiG9w0BAQUDgYEAiaIRqGfp1jPpNeVhABK60SU0KIAy\n" + "njuu7kHq5peUgYn8Jd9zNzExBOEp1VOipGsf6G66oQAhDFp2o8zkz7ZH71zR4HEW\n" + "KoX6n5Emn6DvcEH/9pAhnGxNHJAoS7czTKv/JDZJhkqHxyrE1fuLsg5Qv25DTw7+\n" + "PfqUpIhz5Bbm7J4=\n" + "-----END CERTIFICATE-----\n"; +const gnutls_datum_t ca = { ca_pem, sizeof (ca_pem) }; + +const char cert_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICHjCCAYmgAwIBAgIERiYdNzALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n" + "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTI3WhcNMDgwNDE3MTMyOTI3WjAdMRsw\n" + "GQYDVQQDExJHbnVUTFMgdGVzdCBjbGllbnQwgZwwCwYJKoZIhvcNAQEBA4GMADCB\n" + "iAKBgLtmQ/Xyxde2jMzF3/WIO7HJS2oOoa0gUEAIgKFPXKPQ+GzP5jz37AR2ExeL\n" + "ZIkiW8DdU3w77XwEu4C5KL6Om8aOoKUSy/VXHqLnu7czSZ/ju0quak1o/8kR4jKN\n" + "zj2AC41179gAgY8oBAOgIo1hBAf6tjd9IQdJ0glhaZiQo1ipAgMBAAGjdjB0MAwG\n" + "A1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAweg\n" + "ADAdBgNVHQ4EFgQUTLkKm/odNON+3svSBxX+odrLaJEwHwYDVR0jBBgwFoAU6Twc\n" + "+62SbuYGpFYsouHAUyfI8pUwCwYJKoZIhvcNAQEFA4GBALujmBJVZnvaTXr9cFRJ\n" + "jpfc/3X7sLUsMvumcDE01ls/cG5mIatmiyEU9qI3jbgUf82z23ON/acwJf875D3/\n" + "U7jyOsBJ44SEQITbin2yUeJMIm1tievvdNXBDfW95AM507ShzP12sfiJkJfjjdhy\n" + "dc8Siq5JojruiMizAf0pA7in\n" + "-----END CERTIFICATE-----\n"; +const gnutls_datum_t cert = { cert_pem, sizeof (cert_pem) }; + +int +sign_func (gnutls_session_t session, + void *userdata, + gnutls_certificate_type_t cert_type, + gnutls_datum_t cert, + const gnutls_datum_t hash, + gnutls_datum_t * signature) +{ + gnutls_x509_privkey_t key; + const char key_pem[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQC7ZkP18sXXtozMxd/1iDuxyUtqDqGtIFBACIChT1yj0Phsz+Y8\n" + "9+wEdhMXi2SJIlvA3VN8O+18BLuAuSi+jpvGjqClEsv1Vx6i57u3M0mf47tKrmpN\n" + "aP/JEeIyjc49gAuNde/YAIGPKAQDoCKNYQQH+rY3fSEHSdIJYWmYkKNYqQIDAQAB\n" + "AoGADpmARG5CQxS+AesNkGmpauepiCz1JBF/JwnyiX6vEzUh0Ypd39SZztwrDxvF\n" + "PJjQaKVljml1zkJpIDVsqvHdyVdse8M+Qn6hw4x2p5rogdvhhIL1mdWo7jWeVJTF\n" + "RKB7zLdMPs3ySdtcIQaF9nUAQ2KJEvldkO3m/bRJFEp54k0CQQDYy+RlTmwRD6hy\n" + "7UtMjR0H3CSZJeQ8svMCxHLmOluG9H1UKk55ZBYfRTsXniqUkJBZ5wuV1L+pR9EK\n" + "ca89a+1VAkEA3UmBelwEv2u9cAU1QjKjmwju1JgXbrjEohK+3B5y0ESEXPAwNQT9\n" + "TrDM1m9AyxYTWLxX93dI5QwNFJtmbtjeBQJARSCWXhsoaDRG8QZrCSjBxfzTCqZD\n" + "ZXtl807ymCipgJm60LiAt0JLr4LiucAsMZz6+j+quQbSakbFCACB8SLV1QJBAKZQ\n" + "YKf+EPNtnmta/rRKKvySsi3GQZZN+Dt3q0r094XgeTsAqrqujVNfPhTMeP4qEVBX\n" + "/iVX2cmMTSh3w3z8MaECQEp0XJWDVKOwcTW6Ajp9SowtmiZ3YDYo1LF9igb4iaLv\n" + "sWZGfbnU3ryjvkb6YuFjgtzbZDZHWQCo8/cOtOBmPdk=\n" + "-----END RSA PRIVATE KEY-----\n"; + const gnutls_datum_t key_dat = { key_pem, sizeof (key_pem) }; + int ret; + + ret = gnutls_x509_privkey_init (&key); + if (ret < 0) + return ret; + + ret = gnutls_x509_privkey_import (key, &key_dat, GNUTLS_X509_FMT_PEM); + if (ret < 0) + goto done; + + ret = gnutls_x509_privkey_sign_hash (key, hash, signature); + if (ret < 0) + goto done; + + ret = 0; + + done: + gnutls_x509_privkey_deinit (key); + return ret; +} + +void +client (void) +{ + int ret, sd, ii; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t xcred; + + gnutls_global_init (); + + gnutls_global_set_log_function (tls_log_func); + gnutls_global_set_log_level (4711); + + gnutls_certificate_allocate_credentials (&xcred); + + /* sets the trusted cas file + */ + gnutls_certificate_set_x509_trust_mem (xcred, &ca, GNUTLS_X509_FMT_PEM); + gnutls_certificate_set_x509_key_mem (xcred, &cert, NULL, + GNUTLS_X509_FMT_PEM); + + /* Initialize TLS session + */ + gnutls_init (&session, GNUTLS_CLIENT); + + /* Set sign callback. */ + gnutls_sign_callback_set (session, sign_func, NULL); + + /* Use default priorities */ + gnutls_set_default_priority (session); + + /* put the x509 credentials to the current session + */ + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + /* connect to the peer + */ + sd = tcp_connect (); + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd); + + /* Perform the TLS handshake + */ + ret = gnutls_handshake (session); + + if (ret < 0) + { + fail ("client: Handshake failed\n"); + gnutls_perror (ret); + goto end; + } + else + { + success ("client: Handshake was completed\n"); + } + + success ("client: TLS version is: %s\n", + gnutls_protocol_get_name (gnutls_protocol_get_version (session))); + + /* see the Getting peer's information example */ + print_info(session); + + gnutls_record_send (session, MSG, strlen (MSG)); + + ret = gnutls_record_recv (session, buffer, MAX_BUF); + if (ret == 0) + { + success ("client: Peer has closed the TLS connection\n"); + goto end; + } + else if (ret < 0) + { + fail ("client: Error: %s\n", gnutls_strerror (ret)); + goto end; + } + + printf ("- Received %d bytes: ", ret); + for (ii = 0; ii < ret; ii++) + { + fputc (buffer[ii], stdout); + } + fputs ("\n", stdout); + + gnutls_bye (session, GNUTLS_SHUT_RDWR); + +end: + + tcp_close (sd); + + gnutls_deinit (session); + + gnutls_certificate_free_credentials (xcred); + + gnutls_global_deinit (); +} + +/* This is a sample TLS 1.0 echo server, using X.509 authentication. + */ + +#define SA struct sockaddr +#define MAX_BUF 1024 +#define PORT 5556 /* listen to 5556 port */ +#define DH_BITS 1024 + +/* These are global */ +gnutls_certificate_credentials_t x509_cred; + +gnutls_session_t +initialize_tls_session (void) +{ + gnutls_session_t session; + + gnutls_init (&session, GNUTLS_SERVER); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + gnutls_set_default_priority (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + /* request client certificate if any. + */ + gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); + + gnutls_dh_set_prime_bits (session, DH_BITS); + + return session; +} + +static gnutls_dh_params_t dh_params; + +static int +generate_dh_params (void) +{ + const gnutls_datum_t p3 = { pkcs3, strlen (pkcs3) }; + /* Generate Diffie Hellman parameters - for use with DHE + * kx algorithms. These should be discarded and regenerated + * once a day, once a week or once a month. Depending on the + * security requirements. + */ + gnutls_dh_params_init (&dh_params); + return gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM); +} + +int err, listen_sd, i; +int sd, ret; +struct sockaddr_in sa_serv; +struct sockaddr_in sa_cli; +int client_len; +char topbuf[512]; +gnutls_session_t session; +char buffer[MAX_BUF + 1]; +int optval = 1; + + +const char server_cert_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICVjCCAcGgAwIBAgIERiYdMTALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n" + "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTIxWhcNMDgwNDE3MTMyOTIxWjA3MRsw\n" + "GQYDVQQKExJHbnVUTFMgdGVzdCBzZXJ2ZXIxGDAWBgNVBAMTD3Rlc3QuZ251dGxz\n" + "Lm9yZzCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA17pcr6MM8C6pJ1aqU46o63+B\n" + "dUxrmL5K6rce+EvDasTaDQC46kwTHzYWk95y78akXrJutsoKiFV1kJbtple8DDt2\n" + "DZcevensf9Op7PuFZKBroEjOd35znDET/z3IrqVgbtm2jFqab7a+n2q9p/CgMyf1\n" + "tx2S5Zacc1LWn9bIjrECAwEAAaOBkzCBkDAMBgNVHRMBAf8EAjAAMBoGA1UdEQQT\n" + "MBGCD3Rlc3QuZ251dGxzLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8B\n" + "Af8EBQMDB6AAMB0GA1UdDgQWBBTrx0Vu5fglyoyNgw106YbU3VW0dTAfBgNVHSME\n" + "GDAWgBTpPBz7rZJu5gakViyi4cBTJ8jylTALBgkqhkiG9w0BAQUDgYEAaFEPTt+7\n" + "bzvBuOf7+QmeQcn29kT6Bsyh1RHJXf8KTk5QRfwp6ogbp94JQWcNQ/S7YDFHglD1\n" + "AwUNBRXwd3riUsMnsxgeSDxYBfJYbDLeohNBsqaPDJb7XailWbMQKfAbFQ8cnOxg\n" + "rOKLUQRWJ0K3HyXRMhbqjdLIaQiCvQLuizo=\n" + "-----END CERTIFICATE-----\n"; + +const gnutls_datum_t server_cert = { server_cert_pem, + sizeof (server_cert_pem) }; + +const char server_key_pem[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQDXulyvowzwLqknVqpTjqjrf4F1TGuYvkrqtx74S8NqxNoNALjq\n" + "TBMfNhaT3nLvxqResm62ygqIVXWQlu2mV7wMO3YNlx696ex/06ns+4VkoGugSM53\n" + "fnOcMRP/PciupWBu2baMWppvtr6far2n8KAzJ/W3HZLllpxzUtaf1siOsQIDAQAB\n" + "AoGAYAFyKkAYC/PYF8e7+X+tsVCHXppp8AoP8TEZuUqOZz/AArVlle/ROrypg5kl\n" + "8YunrvUdzH9R/KZ7saNZlAPLjZyFG9beL/am6Ai7q7Ma5HMqjGU8kTEGwD7K+lbG\n" + "iomokKMOl+kkbY/2sI5Czmbm+/PqLXOjtVc5RAsdbgvtmvkCQQDdV5QuU8jap8Hs\n" + "Eodv/tLJ2z4+SKCV2k/7FXSKWe0vlrq0cl2qZfoTUYRnKRBcWxc9o92DxK44wgPi\n" + "oMQS+O7fAkEA+YG+K9e60sj1K4NYbMPAbYILbZxORDecvP8lcphvwkOVUqbmxOGh\n" + "XRmTZUuhBrJhJKKf6u7gf3KWlPl6ShKEbwJASC118cF6nurTjuLf7YKARDjNTEws\n" + "qZEeQbdWYINAmCMj0RH2P0mvybrsXSOD5UoDAyO7aWuqkHGcCLv6FGG+qwJAOVqq\n" + "tXdUucl6GjOKKw5geIvRRrQMhb/m5scb+5iw8A4LEEHPgGiBaF5NtJZLALgWfo5n\n" + "hmC8+G8F0F78znQtPwJBANexu+Tg5KfOnzSILJMo3oXiXhf5PqXIDmbN0BKyCKAQ\n" + "LfkcEcUbVfmDaHpvzwY9VEaoMOKVLitETXdNSxVpvWM=\n" + "-----END RSA PRIVATE KEY-----\n"; + +const gnutls_datum_t server_key = { server_key_pem, + sizeof (server_key_pem) }; + +void +server_start (void) +{ + /* this must be called once in the program + */ + gnutls_global_init (); + + gnutls_global_set_log_function (tls_log_func); + gnutls_global_set_log_level (4711); + + gnutls_certificate_allocate_credentials (&x509_cred); + gnutls_certificate_set_x509_trust_mem (x509_cred, &ca, GNUTLS_X509_FMT_PEM); + + gnutls_certificate_set_x509_key_mem (x509_cred, &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + + success ("Launched, generating DH parameters...\n"); + + generate_dh_params (); + + gnutls_certificate_set_dh_params (x509_cred, dh_params); + + /* Socket operations + */ + listen_sd = socket (AF_INET, SOCK_STREAM, 0); + if (err == -1) + { + perror ("socket"); + fail ("server: socket failed\n"); + return; + } + + memset (&sa_serv, '\0', sizeof (sa_serv)); + sa_serv.sin_family = AF_INET; + sa_serv.sin_addr.s_addr = INADDR_ANY; + sa_serv.sin_port = htons (PORT); /* Server Port number */ + + setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int)); + + err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv)); + if (err == -1) + { + perror ("bind"); + fail ("server: bind failed\n"); + return; + } + + err = listen (listen_sd, 1024); + if (err == -1) + { + perror ("listen"); + fail ("server: listen failed\n"); + return; + } + + success ("server: ready. Listening to port '%d'.\n", PORT); +} + +#include "ex-session-info.c" +#include "ex-x509-info.c" + +void +server (void) +{ + client_len = sizeof (sa_cli); + + session = initialize_tls_session (); + + sd = accept (listen_sd, (SA *) & sa_cli, &client_len); + + success ("server: connection from %s, port %d\n", + inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf, + sizeof (topbuf)), ntohs (sa_cli.sin_port)); + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd); + ret = gnutls_handshake (session); + if (ret < 0) + { + close (sd); + gnutls_deinit (session); + fail ("server: Handshake has failed (%s)\n\n", gnutls_strerror (ret)); + return; + } + success ("server: Handshake was completed\n"); + + success ("server: TLS version is: %s\n", + gnutls_protocol_get_name (gnutls_protocol_get_version (session))); + + /* see the Getting peer's information example */ + print_info(session); + + i = 0; + for (;;) + { + bzero (buffer, MAX_BUF + 1); + ret = gnutls_record_recv (session, buffer, MAX_BUF); + + if (ret == 0) + { + success ("server: Peer has closed the GNUTLS connection\n"); + break; + } + else if (ret < 0) + { + fail ("server: Received corrupted data(%d). Closing...\n", ret); + break; + } + else if (ret > 0) + { + /* echo data back to the client + */ + gnutls_record_send (session, buffer, strlen (buffer)); + } + } + /* do not wait for the peer to close the connection. + */ + gnutls_bye (session, GNUTLS_SHUT_WR); + + close (sd); + gnutls_deinit (session); + + close (listen_sd); + + gnutls_certificate_free_credentials (x509_cred); + + gnutls_global_deinit (); + + success ("server: finished\n"); +} + + +void +doit (void) +{ + server_start (); + if (error_count) + return; + + child = fork (); + if (child < 0) + { + perror ("fork"); + fail ("fork"); + return; + } + + if (child) + { + int status; + /* parent */ + server (); + wait (&status); + } + else + client (); +} |