📄 ssl.c
字号:
#define RESP_0LENGTH "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"/* a tricky test which requires spawning a second server process in * time for a new connection after a 407. */static int apt_post_send(ne_request *req, void *ud, const ne_status *st){ int *code = ud; if (st->code == *code) { struct ssl_server_args args = {SERVER_CERT, NULL}; if (*code == 407) args.numreqs = 2; args.response = RESP_0LENGTH; NE_DEBUG(NE_DBG_HTTP, "Got challenge, awaiting server...\n"); CALL(await_server()); NE_DEBUG(NE_DBG_HTTP, "Spawning proper tunnel server...\n"); /* serve *two* 200 OK responses. */ CALL(spawn_server(7777, serve_tunnel, &args)); NE_DEBUG(NE_DBG_HTTP, "Spawned.\n"); } return OK;}static int apt_creds(void *userdata, const char *realm, int attempt, char *username, char *password){ strcpy(username, "foo"); strcpy(password, "bar"); return attempt;}/* Test for using SSL over a CONNECT tunnel via a proxy server which * requires authentication. Broke briefly between 0.23.x and * 0.24.0. */static int auth_proxy_tunnel(void){ ne_session *sess = ne_session_create("https", "localhost", 443); int ret, code = 407; ne_session_proxy(sess, "localhost", 7777); ne_hook_post_send(sess, apt_post_send, &code); ne_set_proxy_auth(sess, apt_creds, NULL); ne_ssl_trust_cert(sess, def_ca_cert); CALL(spawn_server(7777, single_serve_string, "HTTP/1.0 407 I WANT MORE BISCUITS\r\n" "Proxy-Authenticate: Basic realm=\"bigbluesea\"\r\n" "Connection: close\r\n" "\r\n")); /* run two requests over the tunnel. */ ret = any_2xx_request(sess, "/foobar"); if (!ret) ret = any_2xx_request(sess, "/foobar2"); CALL(await_server()); CALL(ret); ne_session_destroy(sess); return 0;}/* Regression test to check that server credentials aren't sent to the * proxy in a CONNECT request. */static int auth_tunnel_creds(void){ ne_session *sess = ne_session_create("https", "localhost", 443); int ret, code = 401; struct ssl_server_args args = {SERVER_CERT, 0}; ne_session_proxy(sess, "localhost", 7777); ne_hook_post_send(sess, apt_post_send, &code); ne_set_server_auth(sess, apt_creds, NULL); ne_ssl_trust_cert(sess, def_ca_cert); args.response = "HTTP/1.1 401 I want a Shrubbery\r\n" "WWW-Authenticate: Basic realm=\"bigredocean\"\r\n" "Server: Python\r\n" "Content-Length: 0\r\n" "\r\n"; CALL(spawn_server(7777, serve_tunnel, &args)); ret = any_2xx_request(sess, "/foobar"); CALL(await_server()); CALL(ret); ne_session_destroy(sess); return OK; }/* compare against known digest of notvalid.pem. Via: * $ openssl x509 -fingerprint -sha1 -noout -in notvalid.pem */#define THE_DIGEST "cf:5c:95:93:76:c6:3c:01:8b:62:" \ "b1:6f:f7:7f:42:32:ac:e6:69:1b"static int cert_fingerprint(void){ char *fn = ne_concat(srcdir, "/notvalid.pem", NULL); ne_ssl_certificate *cert = ne_ssl_cert_read(fn); char digest[60]; ne_free(fn); ONN("could not load notvalid.pem", cert == NULL); ONN("failed to digest", ne_ssl_cert_digest(cert, digest)); ne_ssl_cert_free(cert); ONV(strcmp(digest, THE_DIGEST), ("digest was %s not %s", digest, THE_DIGEST)); return OK;}/* verify that identity of certificate in filename 'fname' is 'identity' */static int check_identity(const char *fname, const char *identity){ ne_ssl_certificate *cert = ne_ssl_cert_read(fname); const char *id; ONV(cert == NULL, ("could not read cert `%s'", fname)); id = ne_ssl_cert_identity(cert); if (identity) { ONV(id == NULL, ("certificate `%s' had no identity", fname)); ONV(strcmp(id, identity), ("certificate `%s' had identity `%s' not `%s'", fname, id, identity)); } else { ONV(id != NULL, ("certificate `%s' had identity `%s' (expected none)", fname, id)); } ne_ssl_cert_free(cert); return OK;}/* check certificate identities. */static int cert_identities(void){ static const struct { const char *fname, *identity; } certs[] = { { "twocn.cert", "localhost" }, { "altname1.cert", "localhost" }, { "altname2.cert", "nohost.example.com" }, { "altname4.cert", "localhost" }, { "ca4.pem", "fourth.example.com" }, { NULL, NULL } }; int n; for (n = 0; certs[n].fname != NULL; n++) CALL(check_identity(certs[n].fname, certs[n].identity)); return OK;}static int check_validity(const char *fname, const char *from, const char *until){ char actfrom[NE_SSL_VDATELEN], actuntil[NE_SSL_VDATELEN]; ne_ssl_certificate *cert; cert = ne_ssl_cert_read(fname); ONV(cert == NULL, ("could not load cert `%s'", fname)); /* cover all calling combos for nice coverage analysis */ ne_ssl_cert_validity(cert, NULL, NULL); ne_ssl_cert_validity(cert, actfrom, NULL); ne_ssl_cert_validity(cert, NULL, actuntil); ne_ssl_cert_validity(cert, actfrom, actuntil); ONV(strcmp(actfrom, from), ("%s: start time was `%s' not `%s'", fname, actfrom, from)); ONV(strcmp(actuntil, until), ("%s: end time was `%s' not `%s'", fname, actuntil, until)); ne_ssl_cert_free(cert); return OK;}/* ceritificate validity times. */static int cert_validity(void){ char *cert = ne_concat(srcdir, "/expired.pem", NULL); CALL(check_validity(cert, "Jan 21 20:39:04 2002 GMT", "Jan 31 20:39:04 2002 GMT")); ne_free(cert); cert = ne_concat(srcdir, "/notvalid.pem", NULL); CALL(check_validity(cert, "Dec 27 20:40:29 2023 GMT", "Dec 28 20:40:29 2023 GMT")); ne_free(cert); return OK;}/* dname comparisons. */static int dname_compare(void){ ne_ssl_certificate *ssigned; const ne_ssl_dname *dn1, *dn2; dn1 = ne_ssl_cert_subject(def_server_cert); dn2 = ne_ssl_cert_subject(def_server_cert); ONN("identical subject names not equal", ne_ssl_dname_cmp(dn1, dn2) != 0); dn2 = ne_ssl_cert_issuer(def_server_cert); ONN("issuer and subject names equal for signed cert", ne_ssl_dname_cmp(dn1, dn2) == 0); dn1 = ne_ssl_cert_subject(def_ca_cert); ONN("issuer of signed cert not equal to subject of CA cert", ne_ssl_dname_cmp(dn1, dn2) != 0); ssigned = ne_ssl_cert_read("ssigned.pem"); ONN("could not load ssigned.pem", ssigned == NULL); dn1 = ne_ssl_cert_subject(ssigned); dn2 = ne_ssl_cert_issuer(ssigned); ONN("issuer and subject names not equal for self-signed cert", ne_ssl_dname_cmp(dn1, dn2)); ne_ssl_cert_free(ssigned); return OK;}/* The dname with the UTF-8 encoding of the Unicode string: * "H<LATIN SMALL LETTER E WITH GRAVE>llo World". */#define I18N_DNAME "H\xc3\xa8llo World, Neon Hackers Ltd, Cambridge, Cambridgeshire, GB"/* N.B. t61subj.cert encodes an ISO-8859-1 string in a T61String * field, which is strictly wrong but the common usage. *//* tests for ne_ssl_readable_dname */static int dname_readable(void){ struct { const char *cert; const char *subjdn, *issuerdn; } ts[] = { { "justmail.cert", "blah@example.com", NULL }, { "t61subj.cert", I18N_DNAME, NULL }, { "bmpsubj.cert", I18N_DNAME, NULL }, { "utf8subj.cert", I18N_DNAME, NULL } }; size_t n; for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) { ne_ssl_certificate *cert = ne_ssl_cert_read(ts[n].cert); ONV(cert == NULL, ("could not load cert %s", ts[n].cert)); CALL(check_cert_dnames(cert, ts[n].subjdn, ts[n].issuerdn)); ne_ssl_cert_free(cert); } return OK;}/* test cert comparisons */static int cert_compare(void){ ne_ssl_certificate *c1, *c2; c1 = ne_ssl_cert_read("server.cert"); c2 = ne_ssl_cert_read("server.cert"); ONN("identical certs don't compare equal", ne_ssl_cert_cmp(c1, c2) != 0); ONN("identical certs don't compare equal", ne_ssl_cert_cmp(c2, c1) != 0); ne_ssl_cert_free(c2); c2 = ne_ssl_cert_read("ssigned.pem"); ONN("different certs don't compare different", ne_ssl_cert_cmp(c1, c2) == 0); ONN("different certs don't compare different", ne_ssl_cert_cmp(c2, c1) == 0); ne_ssl_cert_free(c2); ne_ssl_cert_free(c1); return OK;}/* Extract raw base64 string from a PEM file */static int flatten_pem(const char *fname, char **out){ FILE *fp = fopen(fname, "r"); char buf[80]; size_t outlen = 0; int ignore = 1; ONV(fp == NULL, ("could not open %s", fname)); *out = NULL; while (fgets(buf, sizeof buf, fp) != NULL) { size_t len = strlen(buf) - 1; if (len < 1) continue; /* look for the wrapper lines. */ if (strncmp(buf, "-----", 5) == 0) { ignore = !ignore; continue; } /* ignore until the first wrapper line */ if (ignore) continue; *out = realloc(*out, outlen + len + 1); memcpy(*out + outlen, buf, len); outlen += len; } (*out)[outlen] = '\0'; fclose(fp); return OK;}/* check export cert data 'actual' against expected data 'expected */static int check_exported_data(const char *actual, const char *expected){ ONN("could not export cert", actual == NULL); ONN("export data contained newline", strchr(actual, '\r') || strchr(actual, '\n')); ONV(strcmp(actual, expected), ("exported cert differed from expected:\n" "actual: %s\nexpected: %s", actual, expected)); return OK;}/* Test import and export of certificates. The export format is PEM * without the line feeds and wrapping; compare against . */static int import_export(void){ char *expected, *actual; ne_ssl_certificate *cert, *imp; CALL(flatten_pem("server.cert", &expected)); cert = ne_ssl_cert_read("server.cert"); ONN("could not load server.cert", cert == NULL); /* export the cert to and compare it with the PEM file */ actual = ne_ssl_cert_export(cert); CALL(check_exported_data(actual, expected)); /* import the exported cert data, check it looks the same */ imp = ne_ssl_cert_import(actual); ONN("failed to import exported cert", imp == NULL); ONN("imported cert was different to original", ne_ssl_cert_cmp(imp, cert)); /* re-export the imported cert and check that looks the same */ ne_free(actual); actual = ne_ssl_cert_export(imp); CALL(check_exported_data(actual, expected)); ne_ssl_cert_free(imp); /* try importing from bogus data */ imp = ne_ssl_cert_import("!!"); ONN("imported bogus cert from bogus base64", imp != NULL); imp = ne_ssl_cert_import("aaaa"); ONN("imported bogus cert from valid base64", imp != NULL); ne_ssl_cert_free(cert); ne_free(actual); ne_free(expected); return OK;}/* Test write/read */static int read_write(void){ ne_ssl_certificate *c1, *c2; c1 = ne_ssl_cert_read("server.cert"); ONN("could not load server.cert", c1 == NULL); ONN("could not write output.pem", ne_ssl_cert_write(c1, "output.pem")); ONN("wrote to nonexistent directory", ne_ssl_cert_write(c1, "nonesuch/output.pem") == 0); c2 = ne_ssl_cert_read("output.pem"); ONN("could not read output.pem", c2 == NULL); ONN("read of output.pem differs from original", ne_ssl_cert_cmp(c2, c1)); ne_ssl_cert_free(c1); ne_ssl_cert_free(c2); return OK;}/* A verification callback which caches the passed cert. */static int verify_cache(void *userdata, int fs, const ne_ssl_certificate *cert){ char **cache = userdata; if (*cache == NULL) { *cache = ne_ssl_cert_export(cert); return 0; } else { return -1; }}/* Test a common use of the SSL API; cache the server cert across * sessions. */static int cache_cert(void){ ne_session *sess = DEFSESS; char *cache = NULL; ne_ssl_certificate *cert; struct ssl_server_args args = {0}; args.cert = "ssigned.pem"; args.cache = 1; ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT, verify_cache, &cache)); ne_session_destroy(sess); ONN("no cert was cached", cache == NULL); /* make a real cert */ cert = ne_ssl_cert_import(cache); ONN("could not import cached cert", cert == NULL); ne_free(cache); /* create a new session */ sess = DEFSESS; /* trust the cert */ ne_ssl_trust_cert(sess, cert); ne_ssl_cert_free(cert); /* now, the request should succeed without manual verification */ ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL)); ne_session_destroy(sess); return OK;}/* TODO: code paths still to test in cert verification: * - server cert changes between connections: Mozilla gives * a "bad MAC decode" error for this; can do better? * - server presents no certificate (using ADH ciphers)... can * only really happen if they mess with the SSL_CTX and enable * ADH cipher manually; but good to check the failure case is * safe. * From the SSL book: * - an early FIN should be returned as a possible truncation attack, * NOT just an NE_SOCK_CLOSED. * - unexpected close_notify is an error but not an attack. * - never attempt session resumption after any aborted connection. */ne_test tests[] = { T_LEAKY(init), T(load_server_certs), T(trust_default_ca), T(cert_fingerprint), T(cert_identities), T(cert_validity), T(cert_compare), T(dname_compare), T(dname_readable), T(import_export), T(read_write), T(load_client_cert), T(simple), T(simple_sslv2), T(simple_eof), T(empty_truncated_eof), T(fail_not_ssl), T(cache_cert), T(client_cert_pkcs12), T(ccert_unencrypted), T(client_cert_provided), T(cc_provided_dnames), T(parse_cert), T(parse_chain), T(no_verify), T(cache_verify), T_LEAKY(wildcard_init), T(wildcard_match), T(caseless_match), T(subject_altname), T(two_subject_altname), T(two_subject_altname2), T(notdns_altname), T(ipaddr_altname), T(multi_commonName), T(commonName_first), T(fail_wrongCN), T(fail_expired), T(fail_notvalid), T(fail_untrusted_ca), T(fail_self_signed), T(fail_missing_CN), T(session_cache), T(fail_tunnel), T(proxy_tunnel), T(auth_proxy_tunnel), T(auth_tunnel_creds), T(NULL) };
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -