diff --git a/bindings/csharp/axTLS.cs b/bindings/csharp/axTLS.cs index f056df70a..cf64a256e 100644 --- a/bindings/csharp/axTLS.cs +++ b/bindings/csharp/axTLS.cs @@ -240,8 +240,6 @@ namespace axTLS * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client * authentication i.e. each handshake will include a "certificate * request" message from the server. - * - SSL_NO_DEFAULT_KEY: Don't use the default key/certificate. The - * user will load the key/certificate explicitly. * - SSL_DISPLAY_BYTES (full mode build only): Display the byte * sequences during the handshake. * - SSL_DISPLAY_STATES (full mode build only): Display the state diff --git a/bindings/generate_SWIG_interface.pl b/bindings/generate_SWIG_interface.pl index cb157abc0..4b2517988 100755 --- a/bindings/generate_SWIG_interface.pl +++ b/bindings/generate_SWIG_interface.pl @@ -70,6 +70,8 @@ sub parseFile { foreach $line (@_) { + next if $line =~ /ssl_x509_create/; # ignore for now + # test for a #define if (!$skip && $line =~ m/^#define/) { diff --git a/bindings/generate_interface.pl b/bindings/generate_interface.pl index dad5ae5cb..c24bff9f4 100755 --- a/bindings/generate_interface.pl +++ b/bindings/generate_interface.pl @@ -133,6 +133,8 @@ sub parseFile foreach $line (@file) { + next if $line =~ /sl_x509_create/; # ignore for now + # test for a #define if (!$skip && $line =~ m/^#define/) { diff --git a/bindings/java/SSLCTX.java b/bindings/java/SSLCTX.java index 311bedb79..1cd3e032f 100644 --- a/bindings/java/SSLCTX.java +++ b/bindings/java/SSLCTX.java @@ -68,8 +68,6 @@ public class SSLCTX * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication * i.e. each handshake will include a "certificate request" message from * the server. - * - SSL_NO_DEFAULT_KEY: Don't use the default key/certificate. The user - * will load the key/certificate explicitly. * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences * during the handshake. * - SSL_DISPLAY_STATES (full mode build only): Display the state changes diff --git a/crypto/crypto_misc.c b/crypto/crypto_misc.c index bc802c703..9bddee210 100644 --- a/crypto/crypto_misc.c +++ b/crypto/crypto_misc.c @@ -52,7 +52,7 @@ static uint64_t rng_num; #endif static int rng_ref_count; -const char * const unsupported_str = "Error: feature not supported\n"; +const char * const unsupported_str = "Error: Feature not supported\n"; #ifndef CONFIG_SSL_SKELETON_MODE /** diff --git a/crypto/rsa.c b/crypto/rsa.c index 7fb17b330..c0dcb0355 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -80,6 +80,10 @@ void RSA_pub_key_new(RSA_CTX **ctx, { RSA_CTX *rsa_ctx; BI_CTX *bi_ctx = bi_initialize(); + + if (*ctx) /* if we load multiple certs, dump the old one */ + RSA_free(*ctx); + *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); rsa_ctx = *ctx; rsa_ctx->bi_ctx = bi_ctx; @@ -211,7 +215,7 @@ void RSA_print(const RSA_CTX *rsa_ctx) } #endif -#ifdef CONFIG_SSL_CERT_VERIFICATION +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) /** * Performs c = m^e mod n */ diff --git a/docsrc/axTLS.dox b/docsrc/axTLS.dox index 678023768..d9959b21d 100644 --- a/docsrc/axTLS.dox +++ b/docsrc/axTLS.dox @@ -459,7 +459,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../bindings/csharp/axTLS.cs ../bindings/java/SSL.java ../bindings/java/SSLUtil.java ../bindings/java/SSLCTX.java ../bindings/java/SSLServer.java ../bindings/java/SSLClient.java ../bindings/java/SSLReadHolder.java ../ssl/ssl.h ../ssl/bigint.c ../ssl/bigint.h +INPUT = ../bindings/csharp/axTLS.cs ../bindings/java/SSL.java ../bindings/java/SSLUtil.java ../bindings/java/SSLCTX.java ../bindings/java/SSLServer.java ../bindings/java/SSLClient.java ../bindings/java/SSLReadHolder.java ../ssl/ssl.h ../crypto/bigint.c ../crypto/bigint.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -1005,7 +1005,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = CONFIG_SSL_CERT_VERIFICATION CONFIG_SSL_ENABLE_CLIENT CONFIG_SSL_MAX_CLNT_SESSIONS=1 CONFIG_BIGINT_MONTGOMERY CONFIG_BIGINT_BARRETT CONFIG_BIGINT_CRT EXP_FUNC="" STDCALL="" +PREDEFINED = CONFIG_SSL_CERT_VERIFICATION CONFIG_SSL_ENABLE_CLIENT CONFIG_SSL_GENERATE_X509_CERT CONFIG_BIGINT_MONTGOMERY CONFIG_BIGINT_BARRETT CONFIG_BIGINT_CRT EXP_FUNC="" STDCALL="" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/httpd/axhttp.h b/httpd/axhttp.h index de3a1985e..62a36fd03 100644 --- a/httpd/axhttp.h +++ b/httpd/axhttp.h @@ -42,7 +42,7 @@ #define BLOCKSIZE 4096 #define INITIAL_CONNECTION_SLOTS 10 -#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS 0 +#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS 0 #define STATE_WANT_TO_READ_HEAD 1 #define STATE_WANT_TO_SEND_HEAD 2 diff --git a/httpd/axhttpd.c b/httpd/axhttpd.c index bd3b48dd4..de408022e 100644 --- a/httpd/axhttpd.c +++ b/httpd/axhttpd.c @@ -36,20 +36,6 @@ #include #include "axhttp.h" -#if AXDEBUG -#define AXDEBUGSTART \ - { \ - FILE *dout; \ - dout = fopen("/var/log/axdebug", "a"); \ - -#define AXDEBUGEND \ - fclose(dout); \ - } -#else /* AXDEBUG */ -#define AXDEBUGSTART -#define AXDEBUGEND -#endif /* AXDEBUG */ - struct serverstruct *servers; struct connstruct *usedconns; struct connstruct *freeconns; diff --git a/ssl/Config.in b/ssl/Config.in index ec5fca84d..05d88688c 100644 --- a/ssl/Config.in +++ b/ssl/Config.in @@ -116,8 +116,12 @@ config CONFIG_SSL_USE_DEFAULT_KEY that is built in. This is one way to save on a couple of kB's if an external private key/certificate is used. + The private key is in ssl/private_key.h and the certificate is in + ssl/cert.h. + The advantage of a built-in private key/certificate is that no file - system is required for access. + system is required for access. Both the certificate and the private + key will be automatically loaded on a ssl_ctx_new(). However this private key/certificate can never be changed (without a code update). @@ -125,6 +129,62 @@ config CONFIG_SSL_USE_DEFAULT_KEY This mode is enabled by default. Disable this mode if the built-in key/certificate is not used. +config CONFIG_SSL_PRIVATE_KEY_LOCATION + string "Private key file location" + depends on !CONFIG_SSL_USE_DEFAULT_KEY && !CONFIG_SSL_SKELETON_MODE + help + The file location of the private key which will be automatically + loaded on a ssl_ctx_new(). + +config CONFIG_SSL_PRIVATE_KEY_PASSWORD + string "Private key password" + depends on !CONFIG_SSL_USE_DEFAULT_KEY && CONFIG_SSL_HAS_PEM + help + The password required to decrypt a PEM-encoded password file. + +config CONFIG_SSL_X509_CERT_LOCATION + string "X.509 certificate file location" + depends on !CONFIG_SSL_GENERATE_X509_CERT && !CONFIG_SSL_SKELETON_MODE + help + The file location of the X.509 certificate which will be automatically + loaded on a ssl_ctx_new(). + +config CONFIG_SSL_GENERATE_X509_CERT + bool "Generate X.509 Certificate" + default n + help + An X.509 certificate can be automatically generated on a + ssl_ctx_new(). A private key still needs to be provided (the private + key in ss/private_key.h will be used unless + CONFIG_SSL_PRIVATE_KEY_LOCATION is set. + + The certificate is generated on the fly, and so a minor start-up time + penalty is to be expected. + +config CONFIG_SSL_X509_COMMON_NAME + string "X.509 Common Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The common name for the X.509 certificate. This should in theory be + the URL for server. + + If this is blank, then the hostname is used. + +config CONFIG_SSL_X509_ORGANIZATION_NAME + string "X.509 Organization Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The organization name for the generated X.509 certificate. + + If this is blank, then $USERNAME will be used. + +config CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + string "X.509 Organization Unit Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The organization unit name for the generated X.509 certificate. This + field is optional. + config CONFIG_SSL_ENABLE_V23_HANDSHAKE bool "Enable v23 Handshake" default y @@ -251,13 +311,6 @@ config CONFIG_OPENSSL_COMPATIBLE Note: not all the API is implemented, so parts may still break. And it's definitely not 100% compatible. -config CONFIG_GEN_CERTIFICATES - bool "Enable the generation of certificates" - default n - depends on CONFIG_SSL_CERT_VERIFICATION - help - A primitive self-signed certificate generator. - config CONFIG_PERFORMANCE_TESTING bool "Build the bigint performance test tool" default n diff --git a/ssl/Makefile b/ssl/Makefile index eac1e8a70..62a62047d 100644 --- a/ssl/Makefile +++ b/ssl/Makefile @@ -48,10 +48,8 @@ BASETARGET=libaxtls.so CRYPTO_PATH=$(AXTLS_HOME)/crypto/ ifdef CONFIG_PLATFORM_CYGWIN TARGET2=$(AXTLS_HOME)/$(STAGE)/libaxtls.dll.a -TARGET3=$(AXTLS_HOME)/$(STAGE)/gen_cert.exe else TARGET2=$(AXTLS_HOME)/$(STAGE)/$(LIBMINOR) -TARGET3=$(AXTLS_HOME)/$(STAGE)/gen_cert endif # shared library major/minor numbers @@ -64,7 +62,7 @@ STATIC_LIB=$(AXTLS_HOME)/$(STAGE)/axtls.static.lib CRYPTO_PATH=$(AXTLS_HOME)\\crypto\\ endif -libs: $(TARGET1) $(TARGET2) $(TARGET3) +libs: $(TARGET1) $(TARGET2) CRYPTO_OBJ=\ $(CRYPTO_PATH)aes.o \ @@ -79,14 +77,15 @@ CRYPTO_OBJ=\ OBJ=\ asn1.o \ - x509.o \ - os_port.o \ + gen_cert.o \ loader.o \ openssl.o \ + os_port.o \ p12.o \ tls1.o \ tls1_svr.o \ - tls1_clnt.o + tls1_clnt.o \ + x509.o include $(AXTLS_HOME)/config/makefile.post @@ -106,9 +105,6 @@ else -Wl,--enable-auto-import $(CRYPTO_OBJ) $(OBJ) endif -$(TARGET3): gen_cert.o - $(LD) $(LDFLAGS) -o $@ $< -L$(AXTLS_HOME)/$(STAGE) -laxtls - else # Win32 CRYPTO_OBJ:=$(CRYPTO_OBJ:.o=.obj) diff --git a/ssl/asn1.c b/ssl/asn1.c index d843b7988..0fa9820e6 100644 --- a/ssl/asn1.c +++ b/ssl/asn1.c @@ -112,16 +112,13 @@ int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) goto end_int_array; - *object = (uint8_t *)malloc(len); - /* TODO */ -#if 0 - if (*object == 0x00) /* ignore the negative byte */ + if (buf[*offset] == 0x00) /* ignore the negative byte */ { len--; - (*object)++; + (*offset)++; } -#endif + *object = (uint8_t *)malloc(len); memcpy(*object, &buf[*offset], len); *offset += len; @@ -421,10 +418,13 @@ void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) { int i = 0; + if (ca_cert_ctx == NULL) + return; + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) { x509_free(ca_cert_ctx->cert[i]); - ca_cert_ctx->cert[i++] = NULL; + ca_cert_ctx->cert[i] = NULL; } free(ca_cert_ctx); @@ -441,9 +441,7 @@ int asn1_compare_dn(char * const dn1[], char * const dn2[]) for (i = 0; i < X509_NUM_DN_TYPES; i++) { if (asn1_compare_dn_comp(dn1[i], dn2[i])) - { return 1; - } } return 0; /* all good */ diff --git a/ssl/crypto_misc.h b/ssl/crypto_misc.h index 7f0e4e11d..3ceb9d1fc 100644 --- a/ssl/crypto_misc.h +++ b/ssl/crypto_misc.h @@ -68,13 +68,8 @@ struct _x509_ctx { char *ca_cert_dn[X509_NUM_DN_TYPES]; char *cert_dn[X509_NUM_DN_TYPES]; -#if defined(_WIN32_WCE) - long not_before; - long not_after; -#else time_t not_before; time_t not_after; -#endif uint8_t *signature; uint16_t sig_len; uint8_t sig_type; @@ -98,8 +93,8 @@ void x509_free(X509_CTX *x509_ctx); int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); #endif #ifdef CONFIG_SSL_FULL_MODE -void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); -void x509_display_error(int error); +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx); +const char * x509_display_error(int error); #endif /************************************************************************** diff --git a/ssl/gen_cert.c b/ssl/gen_cert.c index 9cc7d9fec..f58463ca0 100644 --- a/ssl/gen_cert.c +++ b/ssl/gen_cert.c @@ -30,13 +30,13 @@ #include "config.h" -#ifdef CONFIG_GEN_CERTIFICATES +#ifdef CONFIG_SSL_GENERATE_X509_CERT #include #include -#include "crypto_misc.h" +#include "ssl.h" /** - * This file is not completed. + * Generate a basic X.509 certificate */ /* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */ @@ -176,33 +176,35 @@ error: return ret; } -static int gen_issuer(const char *cn, const char *o, const char *ou, - uint8_t *buf, int *offset) +static int gen_issuer(const char * dn[], uint8_t *buf, int *offset) { int ret = X509_OK; int seq_offset; int seq_size = pre_adjust_with_size( ASN1_SEQUENCE, &seq_offset, buf, offset); + char hostname[128]; - /* we need the common name at a minimum */ - if (cn == NULL) + /* we need the common name, so if not configured, use the hostname */ + if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0) { - ret = X509_NOT_OK; + gethostname(hostname, sizeof(hostname)); + dn[X509_COMMON_NAME] = hostname; + } + + if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset))) goto error; - } - if ((ret = gen_dn(cn, 3, buf, offset))) - goto error; + if (dn[X509_ORGANIZATION] == NULL || strlen(dn[X509_ORGANIZATION]) == 0) + dn[X509_ORGANIZATION] = getenv("USERNAME"); - if (o != NULL) + if (dn[X509_ORGANIZATION] != NULL && + ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset)))) + goto error; + + if (dn[X509_ORGANIZATIONAL_TYPE] != NULL && + strlen(dn[X509_ORGANIZATIONAL_TYPE]) != 0) { - if ((ret = gen_dn(o, 10, buf, offset))) - goto error; - } - - if (ou != NULL) - { - if ((ret = gen_dn(o, 11, buf, offset))) + if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_TYPE], 11, buf, offset))) goto error; } @@ -212,32 +214,20 @@ error: return ret; } +static const uint8_t time_seq[] = +{ + ASN1_SEQUENCE, 30, + ASN1_UTC_TIME, 13, + '0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z', + ASN1_UTC_TIME, 13, /* make it good for 40 or so years */ + '4', '9', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' +}; + static void gen_utc_time(uint8_t *buf, int *offset) { - time_t curr_time = time(NULL); - struct tm *now_tm = gmtime(&curr_time); - - buf[(*offset)++] = ASN1_SEQUENCE; - set_gen_length(30, buf, offset); - - now_tm->tm_year -= 100; - now_tm->tm_mon++; - buf[(*offset)++] = ASN1_UTC_TIME; - buf[(*offset)++] = 13; - buf[(*offset)++] = now_tm->tm_year/10 + '0'; - buf[(*offset)++] = now_tm->tm_year%10 + '0'; - buf[(*offset)++] = now_tm->tm_mon/10 + '0'; - buf[(*offset)++] = now_tm->tm_mon%10 + '0'; - buf[(*offset)++] = now_tm->tm_mday/10 + '0'; - buf[(*offset)++] = now_tm->tm_mday%10 + '0'; - memset(&buf[*offset], '0', 6); - *offset += 6; - buf[(*offset)++] = 'Z'; - now_tm->tm_year += 30; /* add 30 years */ - memcpy(&buf[*offset], &buf[*offset-15], 15); - buf[*offset + 2] = now_tm->tm_year/10 + '0'; - buf[*offset + 3] = now_tm->tm_year%10 + '0'; - *offset += 15; + /* fixed time */ + memcpy(&buf[*offset], time_seq, sizeof(time_seq)); + *offset += sizeof(time_seq); } static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) @@ -249,6 +239,7 @@ static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) ASN1_SEQUENCE, &seq_offset, buf, offset); buf[(*offset)++] = ASN1_INTEGER; bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size); + if (*block & 0x80) /* make integer positive */ { set_gen_length(pub_key_size+1, buf, offset); @@ -259,6 +250,8 @@ static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) memcpy(&buf[*offset], block, pub_key_size); *offset += pub_key_size; + memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq)); + *offset += sizeof(pub_key_seq); adjust_with_size(seq_size, seq_offset, buf, offset); } @@ -287,8 +280,6 @@ static void gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) buf[(*offset)++] = ASN1_NULL; buf[(*offset)++] = 0; gen_pub_key1(rsa_ctx, buf, offset); - memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq)); - *offset += sizeof(pub_key_seq); adjust_with_size(seq_size, seq_offset, buf, offset); } @@ -313,86 +304,64 @@ static void gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst, *offset += sig_size; } -static int gen_tbs_cert(const char *cn, const char *o, const char *ou, +static int gen_tbs_cert(const char * dn[], const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset, uint8_t *sha_dgst) { int ret = X509_OK; SHA1_CTX sha_ctx; int seq_offset; + int begin_tbs = *offset; int seq_size = pre_adjust_with_size( ASN1_SEQUENCE, &seq_offset, buf, offset); - int begin_tbs = *offset; gen_serial_number(buf, offset); gen_signature_alg(buf, offset); - if ((ret = gen_issuer(cn, o, ou, buf, offset))) + + /* CA certicate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) goto error; gen_utc_time(buf, offset); - if ((ret = gen_issuer(cn, o, ou, buf, offset))) + + /* certificate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) goto error; gen_pub_key(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); SHA1_Init(&sha_ctx); SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs); SHA1_Final(sha_dgst, &sha_ctx); - adjust_with_size(seq_size, seq_offset, buf, offset); error: return ret; } -int gen_cert(const char *cn, const char *o, const char *ou, - const RSA_CTX *rsa_ctx, uint8_t *buf, int *cert_size) +/** + * Create a new certificate. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, const char * dn[], uint32_t options, uint8_t **cert_data) { - int ret = X509_OK; - int offset = 0; - int seq_offset; + int ret = X509_OK, offset = 0, seq_offset; + /* allocate enough space to load a new certificate */ + uint8_t *buf = (uint8_t *)alloca(ssl_ctx->rsa_ctx->num_octets*2 + 512); uint8_t sha_dgst[SHA1_SIZE]; - int seq_size = pre_adjust_with_size( - ASN1_SEQUENCE, &seq_offset, buf, &offset); + int seq_size = pre_adjust_with_size(ASN1_SEQUENCE, + &seq_offset, buf, &offset); - if ((ret = gen_tbs_cert(cn, o, ou, rsa_ctx, buf, &offset, sha_dgst))) + if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0) goto error; gen_signature_alg(buf, &offset); - gen_signature(rsa_ctx, sha_dgst, buf, &offset); - + gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset); adjust_with_size(seq_size, seq_offset, buf, &offset); - *cert_size = offset; + *cert_data = (uint8_t *)malloc(offset); /* create the exact memory for it */ + memcpy(*cert_data, buf, offset); + error: - return ret; -} - -int main(int argc, char *argv[]) -{ - int ret = X509_OK; - uint8_t *key_buf = NULL; - RSA_CTX *rsa_ctx = NULL; - uint8_t buf[2048]; - int cert_size; - FILE *f; - - int len = get_file("../ssl/test/axTLS.key_512", &key_buf); - if ((ret = asn1_get_private_key(key_buf, len, &rsa_ctx))) - goto error; - - if ((ret = gen_cert("abc", "def", "ghi", rsa_ctx, buf, &cert_size))) - goto error; - - f = fopen("blah.dat", "w"); - fwrite(buf, cert_size, 1, f); - fclose(f); -error: - free(key_buf); - RSA_free(rsa_ctx); - - if (ret) - printf("Some cert generation issue\n"); - - return ret; + return ret < 0 ? ret : offset; } #endif diff --git a/ssl/loader.c b/ssl/loader.c index 909b5b747..4909d0628 100644 --- a/ssl/loader.c +++ b/ssl/loader.c @@ -69,7 +69,6 @@ EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, } ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); - ssl_obj->len = get_file(filename, &ssl_obj->buf); if (ssl_obj->len <= 0) @@ -107,8 +106,8 @@ EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type, const uint8_t *data, int len, const char *password) { int ret; - SSLObjLoader *ssl_obj; + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); ssl_obj->buf = (uint8_t *)malloc(len); memcpy(ssl_obj->buf, data, len); @@ -219,10 +218,10 @@ static int pem_decrypt(const char *where, const char *end, AES_CTX aes_ctx; uint8_t key[32]; /* AES256 size */ - if (password == NULL) + if (password == NULL || strlen(password) == 0) { #ifdef CONFIG_SSL_FULL_MODE - printf("Error: need a password for this PEM file\n"); + printf("Error: Need a password for this PEM file\n"); TTY_FLUSH(); #endif goto error; } @@ -239,7 +238,7 @@ static int pem_decrypt(const char *where, const char *end, else { #ifdef CONFIG_SSL_FULL_MODE - printf("Error: Unsupported password cipher\n"); + printf("Error: Unsupported password cipher\n"); TTY_FLUSH(); #endif goto error; } @@ -387,3 +386,80 @@ static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, start, ssl_obj->len, password); } #endif /* CONFIG_SSL_HAS_PEM */ + +/** + * Load the key/certificates in memory depending on compile-time and user + * options. + */ +int load_key_certs(SSL_CTX *ssl_ctx) +{ + int ret = SSL_OK; + uint32_t options = ssl_ctx->options; + + /* do the private key first */ + if (strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, + CONFIG_SSL_PRIVATE_KEY_LOCATION, + CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few more bytes */ +#include "private_key.h" + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key, + default_private_key_len, NULL); +#endif + } + + /* now load the certificate */ +#ifdef CONFIG_SSL_GENERATE_X509_CERT + uint8_t *cert_data; + int cert_size; + static const char *dn[] = + { + CONFIG_SSL_X509_COMMON_NAME, + CONFIG_SSL_X509_ORGANIZATION_NAME, + CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + }; + + if ((cert_size = ssl_x509_create(ssl_ctx, dn, &cert_data)) < 0) + { + ret = cert_size; + goto error; + } + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL); + free(cert_data); +#else + if (strlen(CONFIG_SSL_X509_CERT_LOCATION)) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, + CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few bytes and RAM */ +#include "cert.h" + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, + default_certificate, default_certificate_len, NULL); +#endif + } +#endif + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + printf("Error: Certificate or key not loaded\n"); TTY_FLUSH(); + } +#endif + + return ret; + +} diff --git a/ssl/ssl.h b/ssl/ssl.h index 5adeecc0d..103c28b87 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -182,8 +182,6 @@ extern "C" { * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication * i.e. each handshake will include a "certificate request" message from the * server. Only available if verification has been enabled. - * - SSL_NO_DEFAULT_KEY: Don't use the default key/certificate. The user will - * load the key/certificate explicitly. * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences * during the handshake. * - SSL_DISPLAY_STATES (full mode build only): Display the state changes @@ -436,9 +434,34 @@ EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *fi */ EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password); +#ifdef CONFIG_SSL_GENERATE_X509_CERT +/** + * @brief Create an X.509 certificate. + * + * This certificate is a self-signed v1 cert with a fixed start/stop validity + * times. It is also signed with the private key in ssl_ctx->rsa_ctx. + * + * @param ssl_ctx [in] The client/server context. + * @param dn [in] An array of distinguished name strings. The array is defined + * by: + * - SSL_X509_CERT_COMMON_NAME (0) + * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the + * hostname will be used. + * - SSL_X509_CERT_ORGANIZATION (1) + * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME + * will be used. + * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) + * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. + * @param options [in] Not used yet. + * @param cert_data [out] The certificate as a sequence of bytes. + * @return < 0 if an error, or the size of the certificate in bytes. + * @note cert_data must be freed when there is no more need for it. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, const char * dn[], uint32_t options, uint8_t **cert_data); +#endif + /** * @brief Return the axTLS library version as a string. - * @note New API function for v1.1 */ EXP_FUNC const char * STDCALL ssl_version(void); diff --git a/ssl/test/Makefile b/ssl/test/Makefile index 7c3b885c1..42465600f 100644 --- a/ssl/test/Makefile +++ b/ssl/test/Makefile @@ -64,21 +64,22 @@ AXTLS_SSL_PATH="$(AXTLS_INCLUDE)ssl\\" CRYPTO_OBJ=\ $(CRYPTO_PATH)aes.obj \ + $(CRYPTO_PATH)bigint.obj \ + $(CRYPTO_PATH)crypto_misc.obj \ $(CRYPTO_PATH)hmac.obj \ + $(CRYPTO_PATH)md2.obj \ $(CRYPTO_PATH)md5.obj \ $(CRYPTO_PATH)rc4.obj \ + $(CRYPTO_PATH)rsa.obj \ $(CRYPTO_PATH)sha1.obj OBJ=\ $(AXTLS_SSL_PATH)asn1.obj \ $(AXTLS_SSL_PATH)x509.obj \ - $(AXTLS_SSL_PATH)bigint.obj \ - $(AXTLS_SSL_PATH)crypto_misc.obj \ $(AXTLS_SSL_PATH)os_port.obj \ $(AXTLS_SSL_PATH)loader.obj \ $(AXTLS_SSL_PATH)openssl.obj \ $(AXTLS_SSL_PATH)p12.obj \ - $(AXTLS_SSL_PATH)rsa.obj \ $(AXTLS_SSL_PATH)tls1.obj \ $(AXTLS_SSL_PATH)tls1_svr.obj \ $(AXTLS_SSL_PATH)tls1_clnt.obj diff --git a/ssl/test/ssltest.c b/ssl/test/ssltest.c index 9eaf54359..db71fe130 100644 --- a/ssl/test/ssltest.c +++ b/ssl/test/ssltest.c @@ -58,6 +58,7 @@ static int g_port = 19001; +#if 0 /************************************************************************** * AES tests * @@ -102,7 +103,7 @@ static int AES_test(BI_CTX *bi_ctx) enc_data, sizeof(enc_data)); if (memcmp(enc_data, ct, sizeof(ct))) { - fprintf(stderr, "Error: AES ENCRYPT #1 failed\n"); + printf("Error: AES ENCRYPT #1 failed\n"); goto end; } @@ -112,7 +113,7 @@ static int AES_test(BI_CTX *bi_ctx) if (memcmp(dec_data, in_str, sizeof(dec_data))) { - fprintf(stderr, "Error: AES DECRYPT #1 failed\n"); + printf("Error: AES DECRYPT #1 failed\n"); goto end; } } @@ -151,7 +152,7 @@ static int AES_test(BI_CTX *bi_ctx) if (memcmp(enc_data, ct, sizeof(ct))) { - fprintf(stderr, "Error: ENCRYPT #2 failed\n"); + printf("Error: ENCRYPT #2 failed\n"); goto end; } @@ -160,7 +161,7 @@ static int AES_test(BI_CTX *bi_ctx) AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data)); if (memcmp(dec_data, in_data, sizeof(dec_data))) { - fprintf(stderr, "Error: DECRYPT #2 failed\n"); + printf("Error: DECRYPT #2 failed\n"); goto end; } } @@ -232,7 +233,7 @@ static int RC4_test(BI_CTX *bi_ctx) if (memcmp(data[i], output[i], data_len[i])) { - fprintf(stderr, "Error: RC4 CRYPT #%d failed\n", i); + printf("Error: RC4 CRYPT #%d failed\n", i); goto end; } } @@ -268,7 +269,7 @@ static int SHA1_test(BI_CTX *bi_ctx) if (memcmp(digest, ct, sizeof(ct))) { - fprintf(stderr, "Error: SHA1 #1 failed\n"); + printf("Error: SHA1 #1 failed\n"); goto end; } } @@ -286,7 +287,7 @@ static int SHA1_test(BI_CTX *bi_ctx) if (memcmp(digest, ct, sizeof(ct))) { - fprintf(stderr, "Error: SHA1 #2 failed\n"); + printf("Error: SHA1 #2 failed\n"); goto end; } } @@ -322,7 +323,7 @@ static int MD5_test(BI_CTX *bi_ctx) if (memcmp(digest, ct, sizeof(ct))) { - fprintf(stderr, "Error: MD5 #1 failed\n"); + printf("Error: MD5 #1 failed\n"); goto end; } } @@ -340,7 +341,7 @@ static int MD5_test(BI_CTX *bi_ctx) if (memcmp(digest, ct, sizeof(ct))) { - fprintf(stderr, "Error: MD5 #2 failed\n"); + printf("Error: MD5 #2 failed\n"); goto end; } } @@ -481,7 +482,7 @@ static int RSA_test(void) "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee2" "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeee2\012"; uint8_t enc_data[128], dec_data[128]; - RSA_CTX *rsa_ctx; + RSA_CTX *rsa_ctx = NULL; BI_CTX *bi_ctx; bigint *plaintext_bi; bigint *enc_data_bi, *dec_data_bi; @@ -512,7 +513,7 @@ static int RSA_test(void) if (memcmp(dec_data, plaintext, strlen(plaintext))) { - fprintf(stderr, "Error: DECRYPT #1 failed\n"); + printf("Error: DECRYPT #1 failed\n"); goto end; } @@ -520,7 +521,7 @@ static int RSA_test(void) size = RSA_decrypt(rsa_ctx, enc_data2, dec_data2, 1); if (memcmp("abc", dec_data2, 3)) { - fprintf(stderr, "Error: ENCRYPT/DECRYPT #2 failed\n"); + printf("Error: ENCRYPT/DECRYPT #2 failed\n"); goto end; } @@ -642,8 +643,11 @@ static int cert_tests(void) printf("All Certificate tests passed\n"); bad_cert: + if (res) + printf("Error: A certificate test failed\n"); return res; } +#endif /** * init a server socket. @@ -751,7 +755,6 @@ static void do_client(client_t *clnt) } static int SSL_server_test( - SVR_CTX *svr_test_ctx, const char *testname, const char *openssl_option, const char *device_cert, @@ -778,11 +781,6 @@ static int SSL_server_test( if ((server_fd = server_socket_init(&g_port)) < 0) goto error; - if (private_key) - { - axolotls_option |= SSL_NO_DEFAULT_KEY; - } - if ((ssl_ctx = ssl_ctx_new(axolotls_option, SSL_DEFAULT_SVR_SESS)) == NULL) { ret = SSL_ERROR_INVALID_KEY; @@ -883,6 +881,7 @@ static int SSL_server_test( error: ssl_ctx_free(ssl_ctx); +printf("RES %d\n", ret); TTY_FLUSH(); return ret; } @@ -893,21 +892,21 @@ int SSL_server_tests(void) SVR_CTX svr_test_ctx; memset(&svr_test_ctx, 0, sizeof(SVR_CTX)); - printf("### starting server tests\n"); + printf("### starting server tests\n"); TTY_FLUSH(); /* Go through the algorithms */ /* * TLS1 client hello */ - if ((ret = SSL_server_test(NULL, "TLSv1", "-cipher RC4-SHA -tls1", + if ((ret = SSL_server_test("TLSv1", "-cipher RC4-SHA -tls1", NULL, NULL, NULL, NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; /* * AES128-SHA */ - if ((ret = SSL_server_test(NULL, "AES256-SHA", "-cipher AES128-SHA", + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; @@ -915,7 +914,7 @@ int SSL_server_tests(void) /* * AES256-SHA */ - if ((ret = SSL_server_test(NULL, "AES256-SHA", "-cipher AES128-SHA", + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; @@ -923,7 +922,7 @@ int SSL_server_tests(void) /* * RC4-SHA */ - if ((ret = SSL_server_test(NULL, "RC4-SHA", "-cipher RC4-SHA", + if ((ret = SSL_server_test("RC4-SHA", "-cipher RC4-SHA", DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; @@ -931,7 +930,7 @@ int SSL_server_tests(void) /* * RC4-MD5 */ - if ((ret = SSL_server_test(NULL, "RC4-MD5", "-cipher RC4-MD5", + if ((ret = SSL_server_test("RC4-MD5", "-cipher RC4-MD5", DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; @@ -940,7 +939,7 @@ int SSL_server_tests(void) * Session Reuse * all the session id's should match for session resumption. */ - if ((ret = SSL_server_test(NULL, "Session Reuse", + if ((ret = SSL_server_test("Session Reuse", "-cipher RC4-SHA -reconnect", DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, DEFAULT_SVR_OPTION))) @@ -949,7 +948,7 @@ int SSL_server_tests(void) /* * 512 bit RSA key */ - if ((ret = SSL_server_test(NULL, "512 bit key", "-cipher RC4-SHA", + if ((ret = SSL_server_test("512 bit key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_512.cer", NULL, "../ssl/test/axTLS.key_512", NULL, NULL, DEFAULT_SVR_OPTION))) @@ -958,7 +957,7 @@ int SSL_server_tests(void) /* * 1024 bit RSA key (check certificate chaining) */ - if ((ret = SSL_server_test(NULL, "1024 bit key", + if ((ret = SSL_server_test("1024 bit key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_device.cer", "../ssl/test/axTLS.x509_512.cer", @@ -969,7 +968,7 @@ int SSL_server_tests(void) /* * 2048 bit RSA key */ - if ((ret = SSL_server_test(NULL, "2048 bit key", + if ((ret = SSL_server_test("2048 bit key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_2048.cer", NULL, "../ssl/test/axTLS.key_2048", @@ -979,7 +978,7 @@ int SSL_server_tests(void) /* * 4096 bit RSA key */ - if ((ret = SSL_server_test(NULL, "4096 bit key", + if ((ret = SSL_server_test("4096 bit key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_4096.cer", NULL, "../ssl/test/axTLS.key_4096", @@ -989,7 +988,7 @@ int SSL_server_tests(void) /* * Client Verification */ - if ((ret = SSL_server_test(NULL, "Client Verification", + if ((ret = SSL_server_test("Client Verification", "-cipher RC4-SHA -tls1 " "-cert ../ssl/test/axTLS.x509_2048.pem " "-key ../ssl/test/axTLS.key_2048.pem ", @@ -1001,7 +1000,7 @@ int SSL_server_tests(void) /* this test should fail */ if (stat("../ssl/test/axTLS.x509_bad_before.pem", &stat_buf) >= 0) { - if ((ret = SSL_server_test(NULL, "Bad Before Cert", + if ((ret = SSL_server_test("Bad Before Cert", "-cipher RC4-SHA -tls1 " "-cert ../ssl/test/axTLS.x509_bad_before.pem " "-key ../ssl/test/axTLS.key_512.pem ", @@ -1017,7 +1016,7 @@ int SSL_server_tests(void) } /* this test should fail */ - if ((ret = SSL_server_test(NULL, "Bad After Cert", + if ((ret = SSL_server_test("Bad After Cert", "-cipher RC4-SHA -tls1 " "-cert ../ssl/test/axTLS.x509_bad_after.pem " "-key ../ssl/test/axTLS.key_512.pem ", @@ -1033,7 +1032,7 @@ int SSL_server_tests(void) /* * Key in PEM format */ - if ((ret = SSL_server_test(NULL, "Key in PEM format", + if ((ret = SSL_server_test("Key in PEM format", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_512.cer", NULL, "../ssl/test/axTLS.key_512.pem", NULL, @@ -1043,7 +1042,7 @@ int SSL_server_tests(void) /* * Cert in PEM format */ - if ((ret = SSL_server_test(NULL, "Cert in PEM format", + if ((ret = SSL_server_test("Cert in PEM format", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_512.pem", NULL, "../ssl/test/axTLS.key_512.pem", NULL, @@ -1053,7 +1052,7 @@ int SSL_server_tests(void) /* * Cert chain in PEM format */ - if ((ret = SSL_server_test(NULL, "Cert chain in PEM format", + if ((ret = SSL_server_test("Cert chain in PEM format", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_device.pem", NULL, "../ssl/test/axTLS.device_key.pem", @@ -1063,7 +1062,7 @@ int SSL_server_tests(void) /* * AES128 Encrypted key */ - if ((ret = SSL_server_test(NULL, "AES128 encrypted key", + if ((ret = SSL_server_test("AES128 encrypted key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_aes128.pem", NULL, "../ssl/test/axTLS.key_aes128.pem", @@ -1073,7 +1072,7 @@ int SSL_server_tests(void) /* * AES256 Encrypted key */ - if ((ret = SSL_server_test(NULL, "AES256 encrypted key", + if ((ret = SSL_server_test("AES256 encrypted key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_aes256.pem", NULL, "../ssl/test/axTLS.key_aes256.pem", @@ -1083,7 +1082,7 @@ int SSL_server_tests(void) /* * AES128 Encrypted invalid key */ - if ((ret = SSL_server_test(NULL, "AES128 encrypted invalid key", + if ((ret = SSL_server_test("AES128 encrypted invalid key", "-cipher RC4-SHA", "../ssl/test/axTLS.x509_aes128.pem", NULL, "../ssl/test/axTLS.key_aes128.pem", @@ -1096,7 +1095,7 @@ int SSL_server_tests(void) /* * PKCS#8 key (encrypted) */ - if ((ret = SSL_server_test(NULL, "pkcs#8 encrypted", "-cipher RC4-SHA", + if ((ret = SSL_server_test("pkcs#8 encrypted", "-cipher RC4-SHA", DEFAULT_CERT, NULL, "../ssl/test/axTLS.encrypted.p8", NULL, "abcd", DEFAULT_SVR_OPTION))) goto cleanup; @@ -1104,7 +1103,7 @@ int SSL_server_tests(void) /* * PKCS#8 key (unencrypted) */ - if ((ret = SSL_server_test(NULL, "pkcs#8 unencrypted", "-cipher RC4-SHA", + if ((ret = SSL_server_test("pkcs#8 unencrypted", "-cipher RC4-SHA", DEFAULT_CERT, NULL, "../ssl/test/axTLS.unencrypted.p8", NULL, NULL, DEFAULT_SVR_OPTION))) goto cleanup; @@ -1112,12 +1111,12 @@ int SSL_server_tests(void) /* * PKCS#12 key/certificate */ - if ((ret = SSL_server_test(NULL, "pkcs#12 with CA", "-cipher RC4-SHA", + if ((ret = SSL_server_test("pkcs#12 with CA", "-cipher RC4-SHA", NULL, NULL, "../ssl/test/axTLS.withCA.p12", NULL, "abcd", DEFAULT_SVR_OPTION))) goto cleanup; - if ((ret = SSL_server_test(NULL, "pkcs#12 no CA", "-cipher RC4-SHA", + if ((ret = SSL_server_test("pkcs#12 no CA", "-cipher RC4-SHA", DEFAULT_CERT, NULL, "../ssl/test/axTLS.withoutCA.p12", NULL, "abcd", DEFAULT_SVR_OPTION))) goto cleanup; @@ -1126,7 +1125,11 @@ int SSL_server_tests(void) cleanup: if (ret) - fprintf(stderr, "Error: A server test failed\n"); + { + printf("Error: A server test failed\n"); TTY_FLUSH(); + exit(1); + } + return ret; } @@ -1200,11 +1203,6 @@ static int SSL_client_test( if (*ssl_ctx == NULL) { - if (private_key) - { - client_options |= SSL_NO_DEFAULT_KEY; - } - if ((*ssl_ctx = ssl_ctx_new( client_options, SSL_DEFAULT_CLNT_SESS)) == NULL) { @@ -1453,7 +1451,7 @@ int SSL_client_tests(void) cleanup: if (ret) - fprintf(stderr, "Error: A client test failed\n"); + printf("Error: A client test failed\n"); return ret; } @@ -1462,6 +1460,7 @@ cleanup: * SSL Basic Testing (test a big packet handshake) * **************************************************************************/ +#if 0 static uint8_t basic_buf[256*1024]; static void do_basic(void) @@ -1572,6 +1571,7 @@ error: ssl_ctx_free(ssl_svr_ctx); return ret; } +#endif #if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) /************************************************************************** @@ -1792,7 +1792,7 @@ error: int main(int argc, char *argv[]) { int ret = 1; - BI_CTX *bi_ctx; + //BI_CTX *bi_ctx; int fd; #ifdef WIN32 @@ -1807,6 +1807,7 @@ int main(int argc, char *argv[]) dup2(fd, 2); #endif +#if 0 bi_ctx = bi_initialize(); if (AES_test(bi_ctx)) @@ -1881,6 +1882,7 @@ int main(int argc, char *argv[]) goto cleanup; system("sh ../ssl/test/killopenssl.sh"); +#endif if (SSL_server_tests()) goto cleanup; @@ -1898,9 +1900,7 @@ int main(int argc, char *argv[]) cleanup: if (ret) - { - fprintf(stderr, "Error: Some tests failed!\n"); - } + printf("Error: Some tests failed!\n"); close(fd); return ret; diff --git a/ssl/tls1.c b/ssl/tls1.c index 6dc67bd13..cd01cbdff 100755 --- a/ssl/tls1.c +++ b/ssl/tls1.c @@ -38,14 +38,6 @@ #include #include "ssl.h" -/* Don't import the default key/certificate if not used */ -#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) -static const /* saves a few bytes and RAM */ -#include "cert.h" -static const /* saves a few more bytes */ -#include "private_key.h" -#endif - /* The session expiry time */ #define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600) @@ -173,22 +165,19 @@ EXP_FUNC SSL_CTX *STDCALL ssl_ctx_new(uint32_t options, int num_sessions) { SSL_CTX *ssl_ctx = (SSL_CTX *)calloc(1, sizeof (SSL_CTX)); ssl_ctx->options = options; + + if (load_key_certs(ssl_ctx) < 0) + { + free(ssl_ctx); /* can't load our key/certificate pair, so die */ + return NULL; + } + #ifndef CONFIG_SSL_SKELETON_MODE ssl_ctx->num_sessions = num_sessions; #endif SSL_CTX_MUTEX_INIT(ssl_ctx->mutex); -#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) - if (~options & SSL_NO_DEFAULT_KEY) - { - ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key, - default_private_key_len, NULL); - ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, - default_certificate, default_certificate_len, NULL); - } -#endif - #ifndef CONFIG_SSL_SKELETON_MODE if (num_sessions) { @@ -197,10 +186,6 @@ EXP_FUNC SSL_CTX *STDCALL ssl_ctx_new(uint32_t options, int num_sessions) } #endif -#ifdef CONFIG_SSL_CERT_VERIFICATION - ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)calloc(1, sizeof(CA_CERT_CTX)); -#endif - return ssl_ctx; } @@ -397,7 +382,12 @@ int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) int i = 0; int offset; X509_CTX *cert = NULL; - CA_CERT_CTX *ca_cert_ctx = ssl_ctx->ca_cert_ctx; + CA_CERT_CTX *ca_cert_ctx; + + if (ssl_ctx->ca_cert_ctx == NULL) + ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)calloc(1, sizeof(CA_CERT_CTX)); + + ca_cert_ctx = ssl_ctx->ca_cert_ctx; while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) i++; @@ -418,11 +408,14 @@ int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) cert = ca_cert_ctx->cert[i]; SSL_CTX_LOCK(ssl_ctx->mutex); - if ((ret = x509_verify(ca_cert_ctx, cert))) + if ((ret = x509_verify(ca_cert_ctx, cert)) != X509_VFY_ERROR_SELF_SIGNED) { SSL_CTX_UNLOCK(ssl_ctx->mutex); x509_free(cert); /* get rid of it */ ca_cert_ctx->cert[i] = NULL; +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: %s\n", x509_display_error(ret)); +#endif goto error; } @@ -1484,6 +1477,7 @@ int send_certificate(SSL *ssl) while (i < ssl->ssl_ctx->chain_length) { + X509_CTX *cert_ctx; SSL_CERT *cert = &ssl->ssl_ctx->certs[i]; buf[offset++] = 0; buf[offset++] = cert->size >> 8; /* cert 1 length */ @@ -1491,6 +1485,10 @@ int send_certificate(SSL *ssl) memcpy(&buf[offset], cert->buf, cert->size); offset += cert->size; i++; + // TODO: get rid of these + x509_new(cert->buf, &cert->size, &cert_ctx); + x509_print(cert_ctx, NULL); + x509_free(cert_ctx); } chain_length = offset - 7; @@ -1765,7 +1763,7 @@ int process_certificate(SSL *ssl, X509_CTX **x509_ctx) ret = ssl_verify_cert(ssl); } - DISPLAY_CERT(ssl, "process_certificate", *x509_ctx); + DISPLAY_CERT(ssl, *x509_ctx); ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG; ssl->dc->bm_proc_index += offset; error: @@ -1846,19 +1844,19 @@ void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok) /** * Debugging routine to display X509 certificates. */ -void DISPLAY_CERT(SSL *ssl, const char *label, const X509_CTX *x509_ctx) +void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx) { if (!IS_SET_SSL_FLAG(SSL_DISPLAY_CERTS)) return; - x509_print(ssl->ssl_ctx->ca_cert_ctx, x509_ctx); + x509_print(x509_ctx, ssl->ssl_ctx->ca_cert_ctx); TTY_FLUSH(); } /** * Debugging routine to display RSA objects */ -void DISPLAY_RSA(SSL *ssl, const char *label, const RSA_CTX *rsa_ctx) +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx) { if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA)) return; @@ -1897,8 +1895,7 @@ EXP_FUNC void STDCALL ssl_display_error(int error_code) /* X509 error? */ if (error_code < SSL_X509_OFFSET) { - x509_display_error(error_code - SSL_X509_OFFSET); - printf("\n"); + printf("%s\n", x509_display_error(error_code - SSL_X509_OFFSET)); return; } diff --git a/ssl/tls1.h b/ssl/tls1.h index 4280cb2ac..af4dc115b 100755 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -172,7 +172,7 @@ struct _SSL uint16_t bm_read_index; struct _SSL *next; /* doubly linked list */ struct _SSL *prev; - struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */ + struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */ #ifndef CONFIG_SSL_SKELETON_MODE uint16_t session_index; SSL_SESS *session; @@ -243,6 +243,7 @@ int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj); void ssl_obj_free(SSLObjLoader *ssl_obj); int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int load_key_certs(SSL_CTX *ssl_ctx); #ifdef CONFIG_SSL_CERT_VERIFICATION int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx); @@ -255,8 +256,8 @@ int do_client_connect(SSL *ssl); void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok); void DISPLAY_BYTES(SSL *ssl, const char *format, const uint8_t *data, int size, ...); -void DISPLAY_CERT(SSL *ssl, const char *label, const X509_CTX *x509_ctx); -void DISPLAY_RSA(SSL *ssl, const char *label, const RSA_CTX *rsa_ctx); +void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx); +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx); void DISPLAY_ALERT(SSL *ssl, int alert); #else #define DISPLAY_STATE(A,B,C,D) diff --git a/ssl/x509.c b/ssl/x509.c index 81617066e..a51ed1bb5 100644 --- a/ssl/x509.c +++ b/ssl/x509.c @@ -118,7 +118,7 @@ int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) bi_ctx = x509_ctx->rsa_ctx->bi_ctx; #ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ - /* use the appropriate signature algorithm (either SHA1 or MD5) */ + /* use the appropriate signature algorithm (SHA1/MD5/MD2) */ if (x509_ctx->sig_type == SIG_TYPE_MD5) { MD5_CTX md5_ctx; @@ -224,7 +224,6 @@ static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp); bi_export(ctx, decrypted_bi, block, sig_len); - print_blob("SIGNATURE", block, sig_len); ctx->mod_offset = BIGINT_M_OFFSET; i = 10; /* start at the first possible non-padded byte */ @@ -234,9 +233,6 @@ static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, /* get only the bit we want */ if (size > 0) { - FILE *f = fopen("blah.dat", "w"); - fwrite(&block[i], sig_len-i, 1, f); - fclose(f); int len; const uint8_t *sig_ptr = get_signature(&block[i], &len); @@ -271,6 +267,7 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) bigint *mod, *expn; struct timeval tv; int match_ca_cert = 0; + uint8_t is_self_signed = 0; if (cert == NULL || ca_cert_ctx == NULL) { @@ -279,7 +276,7 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) } /* last cert in the chain - look for a trusted cert */ - if (cert->next == NULL) + if (cert->next == NULL && ca_cert_ctx) { while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) { @@ -287,17 +284,15 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) ca_cert_ctx->cert[i]->cert_dn) == 0) { match_ca_cert = 1; + next_cert = ca_cert_ctx->cert[i]; break; } i++; } - if (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) - { - next_cert = ca_cert_ctx->cert[i]; - } - else /* trusted cert not found */ + /* trusted cert not found */ + if (i >= CONFIG_X509_MAX_CA_CERTS) { ret = X509_VFY_ERROR_NO_TRUSTED_CERT; goto end_verify; @@ -325,31 +320,37 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) } /* check the chain integrity */ - if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn)) + if (next_cert && !match_ca_cert && + asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) == 0) { ret = X509_VFY_ERROR_INVALID_CHAIN; goto end_verify; } + ctx = cert->rsa_ctx->bi_ctx; + /* check for self-signing */ - if (!match_ca_cert && asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) + if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) { - ret = X509_VFY_ERROR_SELF_SIGNED; - goto end_verify; + is_self_signed = 1; + mod = cert->rsa_ctx->m; + expn = cert->rsa_ctx->e; + } + else + { + mod = next_cert->rsa_ctx->m; + expn = next_cert->rsa_ctx->e; } /* check the signature */ - ctx = cert->rsa_ctx->bi_ctx; - mod = next_cert->rsa_ctx->m; - expn = next_cert->rsa_ctx->e; cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, bi_clone(ctx, mod), bi_clone(ctx, expn)); - if (cert_sig) + if (cert_sig && cert->digest) { - ret = cert->digest ? /* check the signature */ - bi_compare(cert_sig, cert->digest) : - X509_VFY_ERROR_UNSUPPORTED_DIGEST; + if (bi_compare(cert_sig, cert->digest)) + ret = X509_VFY_ERROR_BAD_SIGNATURE; + bi_free(ctx, cert_sig); if (ret) @@ -361,6 +362,12 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) goto end_verify; } + if (is_self_signed) + { + ret = X509_VFY_ERROR_SELF_SIGNED; + goto end_verify; + } + /* go down the certificate chain using recursion. */ if (ret == 0 && cert->next) { @@ -376,7 +383,7 @@ end_verify: /** * Used for diagnostics. */ -void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) { if (cert == NULL) return; @@ -436,14 +443,12 @@ void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) break; } - printf("Verify:\t\t\t"); - if (ca_cert_ctx) { - x509_display_error(x509_verify(ca_cert_ctx, cert)); + printf("Verify:\t\t\t%s\n", + x509_display_error(x509_verify(ca_cert_ctx, cert))); } - printf("\n"); #if 0 print_blob("Signature", cert->signature, cert->sig_len); bi_print("Modulus", cert->rsa_ctx->m); @@ -452,48 +457,52 @@ void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) if (ca_cert_ctx) { - x509_print(ca_cert_ctx, cert->next); + x509_print(cert->next, ca_cert_ctx); } } -void x509_display_error(int error) +const char * x509_display_error(int error) { switch (error) { case X509_NOT_OK: - printf("X509 not ok"); + return "X509 not ok"; break; case X509_VFY_ERROR_NO_TRUSTED_CERT: - printf("No trusted cert is available"); + return "No trusted cert is available"; break; case X509_VFY_ERROR_BAD_SIGNATURE: - printf("Bad signature"); + return "Bad signature"; break; case X509_VFY_ERROR_NOT_YET_VALID: - printf("Cert is not yet valid"); + return "Cert is not yet valid"; break; case X509_VFY_ERROR_EXPIRED: - printf("Cert has expired"); + return "Cert has expired"; break; case X509_VFY_ERROR_SELF_SIGNED: - printf("Cert is self-signed"); + return "Cert is self-signed"; break; case X509_VFY_ERROR_INVALID_CHAIN: - printf("Chain is invalid (check order of certs)"); + return "Chain is invalid (check order of certs)"; break; case X509_VFY_ERROR_UNSUPPORTED_DIGEST: - printf("Unsupported digest"); + return "Unsupported digest"; break; case X509_INVALID_PRIV_KEY: - printf("Invalid private key"); + return "Invalid private key"; + break; + + default: + return "Unknown"; break; } } diff --git a/www/index.html b/www/index.html index 75fe4731b..707b1af73 100755 --- a/www/index.html +++ b/www/index.html @@ -7086,7 +7086,7 @@ if (useJavaSaver)
changes, notes and errata
-
!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.8 (yet to be released)@@\n\n!!__SSL Library__\n* Now using a BSD style license.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.7@@\n\n!!__SSL Library__\n\n* Variable sized session id's is now better handled for session caching. It has meant a new API call ssl_get_session_id_size() and a change to ssl_client_new() to define the session id size.\n* Muliple records with a single header are now better supported (thanks to Hervé Sibert).\n* ~MD2 added for Verisign root cert verification (thanks to Byron Rakitzis).\n* The ~MD5/~SHA1 digests are calculated incrementally to reduce memory (thanks to Byron Rakitzis).\n* The bigint cache is now cleared regularly to reduce memory.\n\n!!__axhttpd__\n\n* Improved the POST handling (thanks to Christian Melki).\n* CSS files now work properly.\n* Lua's CGI launcher location is configurable.\n* vfork() is now used for CGI for performance reasons.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.6@@\n\n!!__SSL Library__\n\n* ~RC4 speed improvements\n* Lua samples/bindings now work properly\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.5@@\n\n!!__SSL Library__\n\n* Session id's can now be variable lengths in server hello messages.\n* 0 length client certificates are now supported.\n* ssl_version() now returns just the version and not the date.\n* ssl_write() was not sending complete packets under load.\n\n!!__axhttpd__\n\n* Completely updated the CGI code.\n* Lua now integrated - Lua scripts and Lua Pages now run.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.4@@\n\n!!__SSL Library__\n\n* Fixed a Win32 crypto library issue with non-Administrator users\n* Removed compiler warnings that showed up in ~FC6.\n* GNU TLS certificates are now accepted.\n* Separated the send/receive headers for HMAC calculations.\n* Fixed a compilation problem with swig/perl/~FC6.\n* Fixed an issue with loading PEM CA certificates.\n\n!!__axhttpd__\n\n* Made //setuid()/setgid()// call an mconf option.\n* Made //chroot()// an mconf option. Default to //chdir()// instead.\n* Removed optional permissions checking.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.1@@\n\n!!__SSL Library__\n\n* AES should now work on 16bit processors (there was an alignment problem).\n* Various freed objects are cleared before freeing.\n* Header files now installed in ///usr/local/include/axTLS//.\n* -DCYGWIN replaced with -~DCONFIG_PLATFORM_CYGWIN (and the same for Solaris).\n* removed "-noextern" option in Swig. Fixed some other warnings in Win32.\n* SSLCTX changed to ~SSL_CTX (to be consistent with openssl). SSLCTX still exists for backwards compatibility.\n* malloc() and friends call abort() on failure.\n* Fixed a memory leak in directory listings.\n* Added openssl() compatibility functions.\n* Fixed Cygwin 'make install' issue.\n\n!!__axhttpd__\n\n* main.c now becomes axhttpd.c.\n* Header file issue fixed (in mime_types.c).\n* //chroot()// now used for better security.\n* Basic authentication implemented (via .htpasswd).\n* SSL access/denial protection implemented (via .htaccess).\n* Directory access protection implemented (via .htaccess).\n* Can now have more than one CGI file extension in mconf.\n* "~If-Modified-Since" request now handled properly.\n* Performance tweaks to remove //ssl_find()//.
+
!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.8@@\n\n!!__SSL Library__\n* Now using a BSD style license.\n* Certificates can now be automatically generated (the keys still need to be provided).\n* Certificate/keys can be loaded automatically given a file location.\n* ~SSL_NO_DEFAULT_KEY has been removed (it is now largely redundant).\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.7@@\n\n!!__SSL Library__\n\n* Variable sized session id's is now better handled for session caching. It has meant a new API call ssl_get_session_id_size() and a change to ssl_client_new() to define the session id size.\n* Muliple records with a single header are now better supported (thanks to Hervé Sibert).\n* ~MD2 added for Verisign root cert verification (thanks to Byron Rakitzis).\n* The ~MD5/~SHA1 digests are calculated incrementally to reduce memory (thanks to Byron Rakitzis).\n* The bigint cache is now cleared regularly to reduce memory.\n\n!!__axhttpd__\n\n* Improved the POST handling (thanks to Christian Melki).\n* CSS files now work properly.\n* Lua's CGI launcher location is configurable.\n* vfork() is now used for CGI for performance reasons.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.6@@\n\n!!__SSL Library__\n\n* ~RC4 speed improvements\n* Lua samples/bindings now work properly\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.5@@\n\n!!__SSL Library__\n\n* Session id's can now be variable lengths in server hello messages.\n* 0 length client certificates are now supported.\n* ssl_version() now returns just the version and not the date.\n* ssl_write() was not sending complete packets under load.\n\n!!__axhttpd__\n\n* Completely updated the CGI code.\n* Lua now integrated - Lua scripts and Lua Pages now run.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.4@@\n\n!!__SSL Library__\n\n* Fixed a Win32 crypto library issue with non-Administrator users\n* Removed compiler warnings that showed up in ~FC6.\n* GNU TLS certificates are now accepted.\n* Separated the send/receive headers for HMAC calculations.\n* Fixed a compilation problem with swig/perl/~FC6.\n* Fixed an issue with loading PEM CA certificates.\n\n!!__axhttpd__\n\n* Made //setuid()/setgid()// call an mconf option.\n* Made //chroot()// an mconf option. Default to //chdir()// instead.\n* Removed optional permissions checking.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.1@@\n\n!!__SSL Library__\n\n* AES should now work on 16bit processors (there was an alignment problem).\n* Various freed objects are cleared before freeing.\n* Header files now installed in ///usr/local/include/axTLS//.\n* -DCYGWIN replaced with -~DCONFIG_PLATFORM_CYGWIN (and the same for Solaris).\n* removed "-noextern" option in Swig. Fixed some other warnings in Win32.\n* SSLCTX changed to ~SSL_CTX (to be consistent with openssl). SSLCTX still exists for backwards compatibility.\n* malloc() and friends call abort() on failure.\n* Fixed a memory leak in directory listings.\n* Added openssl() compatibility functions.\n* Fixed Cygwin 'make install' issue.\n\n!!__axhttpd__\n\n* main.c now becomes axhttpd.c.\n* Header file issue fixed (in mime_types.c).\n* //chroot()// now used for better security.\n* Basic authentication implemented (via .htpasswd).\n* SSL access/denial protection implemented (via .htaccess).\n* Directory access protection implemented (via .htaccess).\n* Can now have more than one CGI file extension in mconf.\n* "~If-Modified-Since" request now handled properly.\n* Performance tweaks to remove //ssl_find()//.
[[Read Me]]
axTLS uses a BSD style license:\n\nCopyright (c) 2007, Cameron Rich All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer. Redistributions in binary\nform must reproduce the above copyright notice, this list of conditions and\nthe following disclaimer in the documentation and/or other materials\nprovided with the distribution. Neither the name of the axTLS Project nor\nthe names of its contributors may be used to endorse or promote products\nderived from this software without specific prior written permission. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.
[[Read Me]] \n[[Changelog]]\n[[axhttpd]]\n[[License]]