📄 ssl.c
字号:
/* neon test suite Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk> This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "config.h"#include <sys/types.h>#include <sys/stat.h>#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "ne_request.h"#include "ne_socket.h"#include "ne_auth.h"#include "tests.h"#include "child.h"#include "utils.h"#ifndef NEON_SSL/* this file shouldn't be built if SSL is not enabled. */#error SSL not supported#endif#include <openssl/ssl.h>#include <openssl/err.h>#include <openssl/evp.h>#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))#define SERVER_CERT "server.cert"#define CA_CERT "ca/cert.pem"#define SERVER_DNAME "Neon QA Dept, Neon Hackers Ltd, " \ "Cambridge, Cambridgeshire, GB"#define CACERT_DNAME "Random Dept, Neosign, Oakland, California, US"static char *srcdir = ".";static char *server_key = NULL; static ne_ssl_certificate *def_ca_cert = NULL, *def_server_cert;static ne_ssl_client_cert *def_cli_cert;static int check_dname(const ne_ssl_dname *dn, const char *expected, const char *which);static int s_strwrite(SSL *s, const char *buf){ size_t len = strlen(buf); ONV(SSL_write(s, buf, len) != (int)len, ("SSL_write failed: %s", ERROR_SSL_STRING)); return OK;}/* Arguments for running the SSL server */struct ssl_server_args { char *cert; /* the server cert to present. */ const char *response; /* the response to send. */ int unclean; /* use an unclean shutdown if non-NULL */ int numreqs; /* number of request/responses to handle over the SSL connection. */ /* client cert handling: */ int require_cc; /* require a client cert if non-NULL */ const char *ca_list; /* file of CA certs to verify client cert against */ const char *send_ca; /* file of CA certs to send in client cert request */ /* session caching: */ int cache; /* use the session cache if non-zero */ SSL_SESSION *session; /* use to store copy of cached session. */ int count; /* internal use. */ int use_ssl2; /* force use of SSLv2 only */};/* default response string if args->response is NULL */#define DEF_RESP "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"/* An SSL server inna bun. */static int ssl_server(ne_socket *sock, void *userdata){ struct ssl_server_args *args = userdata; int fd = ne_sock_fd(sock), ret; /* we don't want OpenSSL to close this socket for us. */ BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE); char buf[BUFSIZ]; SSL *ssl; static SSL_CTX *ctx = NULL; if (ctx == NULL) { ctx = SSL_CTX_new(args->use_ssl2 ? SSLv2_server_method() : SSLv23_server_method()); } ONV(ctx == NULL, ("could not create SSL_CTX: %s", ERROR_SSL_STRING)); ONV(!SSL_CTX_use_PrivateKey_file(ctx, server_key, SSL_FILETYPE_PEM), ("failed to load private key: %s", ERROR_SSL_STRING)); NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", args->cert); ONN("failed to load certificate", !SSL_CTX_use_certificate_file(ctx, args->cert, SSL_FILETYPE_PEM)); if (args->require_cc) { /* require a client cert. */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); if (args->send_ca) { /* the list of issuer DNs of these CAs is included in the * certificate request message sent to the client */ SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(args->send_ca)); } /* set default ca_list */ if (!args->ca_list) args->ca_list = CA_CERT; } if (args->ca_list) { /* load the CA used to verify the client cert; also sent in the * certificate exchange message. */ ONN("failed to load CA cert", SSL_CTX_load_verify_locations(ctx, args->ca_list, NULL) != 1); } if (args->cache && args->count == 0) { /* enable OpenSSL's internal session cache, enabling the * negotiation to re-use a session if both sides support it. */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); } ssl = SSL_new(ctx); ONN("SSL_new failed", ssl == NULL); args->count++; SSL_set_bio(ssl, bio, bio); ONV(SSL_accept(ssl) != 1, ("SSL_accept failed: %s", ERROR_SSL_STRING)); /* loop handling requests: */ do { const char *response = args->response ? args->response : DEF_RESP; ret = SSL_read(ssl, buf, BUFSIZ - 1); if (ret == 0) return 0; /* connection closed by parent; give up. */ ONV(ret < 0, ("SSL_read failed (%d): %s", ret, ERROR_SSL_STRING)); buf[ret] = '\0'; NE_DEBUG(NE_DBG_HTTP, "Request over SSL was: [%s]\n", buf); if (strstr(buf, "Proxy-Authorization:") != NULL) { NE_DEBUG(NE_DBG_HTTP, "Got Proxy-Auth header over SSL!\n"); response = "HTTP/1.1 500 Client Leaks Credentials\r\n" "Content-Length: 0\r\n" "\r\n"; } CALL(s_strwrite(ssl, response)); } while (--args->numreqs > 0); /* copy out the session if requested. */ if (args->session) { SSL_SESSION *sess = SSL_get1_session(ssl);#ifdef NE_DEBUGGING /* dump session to child.log for debugging. */ SSL_SESSION_print_fp(ne_debug_stream, sess);#endif if (args->count == 0) { /* save the session */ args->session = sess; } else { /* could just to do this with SSL_CTX_sess_hits really, * but this is a more thorough test. */ ONN("cached SSL session not used", SSL_SESSION_cmp(args->session, sess)); SSL_SESSION_free(args->session); SSL_SESSION_free(sess); } } if (!args->unclean) { /* Erk, shutdown is messy! See Eric Rescorla's article: * http://www.linuxjournal.com/article.php?sid=4822 ; we'll just * hide our heads in the sand here. */ SSL_shutdown(ssl); SSL_free(ssl); } return 0;}/* serve_ssl wrapper which ignores server failure and always succeeds */static int fail_serve(ne_socket *sock, void *ud){ struct ssl_server_args args = {0}; args.cert = ud; ssl_server(sock, &args); return OK;}#define DEFSESS (ne_session_create("https", "localhost", 7777))/* Run a request in the given session. */static int any_ssl_request(ne_session *sess, server_fn fn, void *server_ud, char *ca_cert, ne_ssl_verify_fn verify_fn, void *verify_ud){ int ret; if (ca_cert) { ne_ssl_certificate *ca = ne_ssl_cert_read(ca_cert); ONV(ca == NULL, ("could not load CA cert `%s'", ca_cert)); ne_ssl_trust_cert(sess, ca); ne_ssl_cert_free(ca); } CALL(spawn_server(7777, fn, server_ud)); if (verify_fn) ne_ssl_set_verify(sess, verify_fn, verify_ud); ret = any_request(sess, "/foo"); CALL(await_server()); ONREQ(ret); return OK;}static int init(void){ /* take srcdir as argv[1]. */ if (test_argc > 1) { srcdir = test_argv[1]; server_key = ne_concat(srcdir, "/server.key", NULL); } else { server_key = "server.key"; } if (ne_sock_init()) { t_context("could not initialize socket/SSL library."); return FAILHARD; } def_ca_cert = ne_ssl_cert_read(CA_CERT); if (def_ca_cert == NULL) { t_context("couldn't load CA cert %s", CA_CERT); return FAILHARD; } def_server_cert = ne_ssl_cert_read(SERVER_CERT); if (def_server_cert == NULL) { t_context("couldn't load server cert %s", SERVER_CERT); return FAILHARD; } /* tests for the encrypted client cert, client.p12 */ def_cli_cert = ne_ssl_clicert_read("client.p12"); ONN("could not load client.p12", def_cli_cert == NULL); ONN("client.p12 is not encrypted!?", !ne_ssl_clicert_encrypted(def_cli_cert)); ONN("failed to decrypt client.p12", ne_ssl_clicert_decrypt(def_cli_cert, "foobar")); return OK;}/* just check the result codes of loading server certs. */static int load_server_certs(void){ ne_ssl_certificate *cert; cert = ne_ssl_cert_read("Makefile"); ONN("invalid CA cert file loaded successfully", cert != NULL); cert = ne_ssl_cert_read("nonesuch.pem"); ONN("non-existent 'nonesuch.pem' loaded successfully", cert != NULL); cert = ne_ssl_cert_read("ssigned.pem"); ONN("could not load ssigned.pem", cert == NULL); ne_ssl_cert_free(cert); return OK;}static int trust_default_ca(void){ ne_session *sess = DEFSESS; ne_ssl_trust_default_ca(sess); ne_session_destroy(sess); return OK;}#define CC_NAME "Just A Neon Client Cert"/* Tests for loading client certificates */static int load_client_cert(void){ ne_ssl_client_cert *cc; const ne_ssl_certificate *cert; const char *name; cc = ne_ssl_clicert_read("client.p12"); ONN("could not load client.p12", cc == NULL); ONN("client.p12 not encrypted!?", !ne_ssl_clicert_encrypted(cc)); name = ne_ssl_clicert_name(cc); ONN("no friendly name given", name == NULL); ONV(strcmp(name, CC_NAME), ("friendly name was %s not %s", name, CC_NAME)); ONN("failed to decrypt", ne_ssl_clicert_decrypt(cc, "foobar")); ne_ssl_clicert_free(cc); cc = ne_ssl_clicert_read("client.p12"); ONN("decrypted client.p12 with incorrect password!?", ne_ssl_clicert_decrypt(cc, "barfoo") == 0); ne_ssl_clicert_free(cc); /* tests for the unencrypted client cert, client2.p12 */ cc = ne_ssl_clicert_read("unclient.p12"); ONN("could not load unencrypted cert unclient.p12", cc == NULL); ONN("unencrypted cert marked encrypted?", ne_ssl_clicert_encrypted(cc)); cert = ne_ssl_clicert_owner(cc); ONN("client cert had no certificate", cert == NULL); CALL(check_dname(ne_ssl_cert_subject(cert), "Neon Client Cert, Neon Hackers Ltd, " "Cambridge, Cambridgeshire, GB", "client cert subject")); CALL(check_dname(ne_ssl_cert_issuer(cert), CACERT_DNAME, "client cert issuer")); ne_ssl_clicert_free(cc); /* test for ccert without a friendly name, noclient.p12 */ cc = ne_ssl_clicert_read("noclient.p12"); ONN("could not load noclient.p12", cc == NULL); name = ne_ssl_clicert_name(cc); ONV(name != NULL, ("noclient.p12 had friendly name `%s'", name)); ne_ssl_clicert_free(cc); /* tests for loading bogus files. */ cc = ne_ssl_clicert_read("Makefile"); ONN("loaded Makefile as client cert!?", cc != NULL); /* test for loading nonexistent file. */ cc = ne_ssl_clicert_read("nosuch.pem"); ONN("loaded nonexistent file as client cert!?", cc != NULL); return OK;}/* Test that 'cert', which is signed by CA_CERT, is accepted * unconditionaly. */static int accept_signed_cert_for_hostname(char *cert, const char *hostname){ ne_session *sess = ne_session_create("https", hostname, 7777); struct ssl_server_args args = {cert, 0}; /* no verify callback needed. */ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}static int accept_signed_cert(char *cert){ return accept_signed_cert_for_hostname(cert, "localhost");}static int simple(void){ return accept_signed_cert(SERVER_CERT);}/* Test for SSL operation when server uses SSLv2 */static int simple_sslv2(void){ ne_session *sess = ne_session_create("https", "localhost", 7777); struct ssl_server_args args = {SERVER_CERT, 0}; args.use_ssl2 = 1; CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}/* Serves using HTTP/1.0 get-till-EOF semantics. */static int serve_eof(ne_socket *sock, void *ud){ struct ssl_server_args args = {0}; args.cert = ud; args.response = "HTTP/1.0 200 OK\r\n" "Connection: close\r\n" "\r\n" "This is a response body, like it or not."; return ssl_server(sock, &args);}/* Test read-til-EOF behaviour with SSL. */static int simple_eof(void){ ne_session *sess = DEFSESS; CALL(any_ssl_request(sess, serve_eof, SERVER_CERT, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}static int empty_truncated_eof(void){ ne_session *sess = DEFSESS; struct ssl_server_args args = {0}; args.cert = SERVER_CERT; args.response = "HTTP/1.0 200 OK\r\n" "\r\n"; CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}/* Server function which just sends a string then EOF. */static int just_serve_string(ne_socket *sock, void *userdata){ const char *str = userdata; server_send(sock, str, strlen(str)); return 0;}/* test for the SSL negotiation failing. */static int fail_not_ssl(void){ ne_session *sess = DEFSESS; int ret; CALL(spawn_server(7777, just_serve_string, "Hello, world.\n")); ret = any_request(sess, "/bar"); CALL(await_server()); ONN("request did not fail", ret != NE_ERROR); ne_session_destroy(sess); return OK;}static int wildcard_ok = 0; static int wildcard_init(void){ struct stat stbuf; t_context("wildcard.cert not found:\n" "This test requires a Linux-like hostname command, see makekeys.sh"); PRECOND(stat("wildcard.cert", &stbuf) == 0); PRECOND(lookup_hostname() == OK); wildcard_ok = 1; return OK;}static int wildcard_match(void){ ne_session *sess; struct ssl_server_args args = {"wildcard.cert", 0}; PRECOND(wildcard_ok); sess = ne_session_create("https", local_hostname, 7777); CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}/* Check that hostname comparisons are not cases-sensitive. */static int caseless_match(void){ return accept_signed_cert("caseless.cert");}/* Test that the subjectAltName extension has precedence over the * commonName attribute */static int subject_altname(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -