diff options
-rw-r--r-- | configure.ac | 10 | ||||
-rwxr-xr-x | scripts/test.sh | 2 | ||||
-rw-r--r-- | src/libstrongswan/Makefile.am | 7 | ||||
-rw-r--r-- | src/libstrongswan/plugins/winhttp/Makefile.am | 18 | ||||
-rw-r--r-- | src/libstrongswan/plugins/winhttp/winhttp_fetcher.c | 396 | ||||
-rw-r--r-- | src/libstrongswan/plugins/winhttp/winhttp_fetcher.h | 46 | ||||
-rw-r--r-- | src/libstrongswan/plugins/winhttp/winhttp_plugin.c | 74 | ||||
-rw-r--r-- | src/libstrongswan/plugins/winhttp/winhttp_plugin.h | 42 | ||||
-rw-r--r-- | src/libstrongswan/tests/suites/test_fetch_http.c | 2 | ||||
-rw-r--r-- | src/pki/commands/verify.c | 134 |
10 files changed, 678 insertions, 53 deletions
diff --git a/configure.ac b/configure.ac index 736f097c1..87aa40dd9 100644 --- a/configure.ac +++ b/configure.ac @@ -161,6 +161,7 @@ ARG_ENABL_SET([curl], [enable CURL fetcher plugin to fetch files via l ARG_ENABL_SET([ldap], [enable LDAP fetching plugin to fetch files via libldap. Requires openLDAP.]) ARG_ENABL_SET([soup], [enable soup fetcher plugin to fetch from HTTP via libsoup. Requires libsoup.]) ARG_ENABL_SET([unbound], [enable UNBOUND resolver plugin to perform DNS queries via libunbound. Requires libldns and libunbound.]) +ARG_ENABL_SET([winhttp], [enable WinHTTP based HTTP/HTTPS fetching plugin.]) # database plugins ARG_ENABL_SET([mysql], [enable MySQL database support. Requires libmysqlclient_r.]) ARG_ENABL_SET([sqlite], [enable SQLite database support. Requires libsqlite3.]) @@ -1159,8 +1160,9 @@ s_plugins= t_plugins= ADD_PLUGIN([test-vectors], [s charon scepclient pki]) -ADD_PLUGIN([curl], [s charon scepclient scripts nm cmd]) -ADD_PLUGIN([soup], [s charon scripts nm cmd]) +ADD_PLUGIN([curl], [s charon scepclient pki scripts nm cmd]) +ADD_PLUGIN([winhttp], [s charon pki scripts]) +ADD_PLUGIN([soup], [s charon pki scripts nm cmd]) ADD_PLUGIN([unbound], [s charon scripts]) ADD_PLUGIN([ldap], [s charon scepclient scripts nm cmd]) ADD_PLUGIN([mysql], [s charon pool manager medsrv attest]) @@ -1178,7 +1180,7 @@ ADD_PLUGIN([rdrand], [s charon scepclient pki scripts medsrv attes ADD_PLUGIN([random], [s charon scepclient pki scripts medsrv attest nm cmd aikgen]) ADD_PLUGIN([nonce], [s charon nm cmd aikgen]) ADD_PLUGIN([x509], [s charon scepclient pki scripts attest nm cmd aikgen]) -ADD_PLUGIN([revocation], [s charon nm cmd]) +ADD_PLUGIN([revocation], [s charon pki nm cmd]) ADD_PLUGIN([constraints], [s charon nm cmd]) ADD_PLUGIN([acert], [s charon]) ADD_PLUGIN([pubkey], [s charon cmd aikgen]) @@ -1305,6 +1307,7 @@ AC_SUBST(t_plugins) # ----------------------- AM_CONDITIONAL(USE_TEST_VECTORS, test x$test_vectors = xtrue) AM_CONDITIONAL(USE_CURL, test x$curl = xtrue) +AM_CONDITIONAL(USE_WINHTTP, test x$winhttp = xtrue) AM_CONDITIONAL(USE_UNBOUND, test x$unbound = xtrue) AM_CONDITIONAL(USE_SOUP, test x$soup = xtrue) AM_CONDITIONAL(USE_LDAP, test x$ldap = xtrue) @@ -1571,6 +1574,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/sshkey/Makefile src/libstrongswan/plugins/pem/Makefile src/libstrongswan/plugins/curl/Makefile + src/libstrongswan/plugins/winhttp/Makefile src/libstrongswan/plugins/unbound/Makefile src/libstrongswan/plugins/soup/Makefile src/libstrongswan/plugins/ldap/Makefile diff --git a/scripts/test.sh b/scripts/test.sh index 103b41103..7c1dc89ac 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -35,7 +35,7 @@ all) --disable-lock-profiler --disable-maemo --disable-padlock --disable-osx-attr --disable-tkm --disable-uci --disable-aikgen --disable-svc --disable-dbghelp-backtraces --disable-socket-win - --disable-kernel-wfp --disable-kernel-iph" + --disable-kernel-wfp --disable-kernel-iph --disable-winhttp" if test "$LEAK_DETECTIVE" = "yes"; then # libgcrypt can't be deinitialized CONFIG="$CONFIG --disable-gcrypt" diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 2602a9eba..9a08825fd 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -425,6 +425,13 @@ if MONOLITHIC endif endif +if USE_WINHTTP + SUBDIRS += plugins/winhttp +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/winhttp/libstrongswan-winhttp.la +endif +endif + if USE_UNBOUND SUBDIRS += plugins/unbound if MONOLITHIC diff --git a/src/libstrongswan/plugins/winhttp/Makefile.am b/src/libstrongswan/plugins/winhttp/Makefile.am new file mode 100644 index 000000000..f6b00a71e --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/Makefile.am @@ -0,0 +1,18 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-winhttp.la +else +plugin_LTLIBRARIES = libstrongswan-winhttp.la +endif + +libstrongswan_winhttp_la_SOURCES = \ + winhttp_fetcher.c winhttp_fetcher.h \ + winhttp_plugin.c winhttp_plugin.h + +libstrongswan_winhttp_la_LDFLAGS = -module -avoid-version +libstrongswan_winhttp_la_LIBADD = -lwinhttp diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c new file mode 100644 index 000000000..4a822bd9e --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +#include <winsock2.h> +#include <windows.h> +#include <winhttp.h> + +#include "winhttp_fetcher.h" + +#include <library.h> + +/** + * Timeout for DNS resolution, in ms + */ +#define RESOLVE_TIMEOUT 5000 + +/** + * Timeout for TCP connect, in ms + */ +#define CONNECT_TIMEOUT 10000 + +typedef struct private_winhttp_fetcher_t private_winhttp_fetcher_t; + +/** + * Private data of a winhttp_fetcher_t. + */ +struct private_winhttp_fetcher_t { + + /** + * Public interface + */ + winhttp_fetcher_t public; + + /** + * WinHTTP session handle + */ + HINTERNET session; + + /** + * POST request data + */ + chunk_t request; + + /** + * HTTP version string to use + */ + LPWSTR version; + + /** + * Optional HTTP headers, as allocated LPWSTR + */ + linked_list_t *headers; + + /** + * Callback function + */ + fetcher_callback_t cb; + + /** + * Timeout for operations, in ms + */ + u_long timeout; + + /** + * User pointer to store HTTP status code to + */ + u_int *result; +}; + +/** + * Configure and send the HTTP request + */ +static bool send_request(private_winhttp_fetcher_t *this, HINTERNET request) +{ + WCHAR headers[512] = L""; + LPWSTR hdr; + + /* Set timeout. By default, send/receive does not time out */ + if (!WinHttpSetTimeouts(request, RESOLVE_TIMEOUT, CONNECT_TIMEOUT, + this->timeout, this->timeout)) + { + DBG1(DBG_LIB, "opening HTTP request failed: %u", GetLastError()); + return FALSE; + } + while (this->headers->remove_first(this->headers, (void**)&hdr) == SUCCESS) + { + wcsncat(headers, hdr, countof(headers) - wcslen(headers) - 1); + if (this->headers->get_count(this->headers)) + { + wcsncat(headers, L"\r\n", countof(headers) - wcslen(headers) - 1); + } + free(hdr); + } + if (!WinHttpSendRequest(request, headers, wcslen(headers), + this->request.ptr, this->request.len, this->request.len, 0)) + { + DBG1(DBG_LIB, "sending HTTP request failed: %u", GetLastError()); + return FALSE; + } + return TRUE; +} + +/** + * Read back result and invoke receive callback + */ +static bool read_result(private_winhttp_fetcher_t *this, HINTERNET request, + void *user) +{ + DWORD received; + char buf[1024]; + u_int32_t code; + DWORD codelen = sizeof(code); + + if (!WinHttpReceiveResponse(request, NULL)) + { + DBG1(DBG_LIB, "reading HTTP response header failed: %u", GetLastError()); + return FALSE; + } + if (!WinHttpQueryHeaders(request, + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + NULL, &code, &codelen, NULL)) + { + DBG1(DBG_LIB, "reading HTTP status code failed: %u", GetLastError()); + return FALSE; + } + if (this->result) + { + *this->result = code; + } + if (code < 200 || code >= 300) + { /* non-successful HTTP status code */ + if (!this->result) + { + DBG1(DBG_LIB, "HTTP request failed with status %u", code); + } + return FALSE; + } + if (this->cb == fetcher_default_callback) + { + *(chunk_t*)user = chunk_empty; + } + while (TRUE) + { + if (!WinHttpReadData(request, buf, sizeof(buf), &received)) + { + DBG1(DBG_LIB, "reading HTTP response failed: %u", GetLastError()); + return FALSE; + } + if (received == 0) + { + /* end of response */ + break; + } + if (!this->cb(user, chunk_create(buf, received))) + { + DBG1(DBG_LIB, "processing response failed or cancelled"); + return FALSE; + } + } + return TRUE; +} + +/** + * Parse an uri to wide string host and path, optionally set flags and port + */ +static bool parse_uri(private_winhttp_fetcher_t *this, char *uri, + LPWSTR host, int hostlen, LPWSTR path, int pathlen, + LPWSTR user, int userlen, LPWSTR pass, int passlen, + DWORD *flags, INTERNET_PORT *port) +{ + WCHAR wuri[512], extra[256]; + URL_COMPONENTS comps = { + .dwStructSize = sizeof(URL_COMPONENTS), + .lpszHostName = host, + .dwHostNameLength = hostlen, + .lpszUrlPath = path, + .dwUrlPathLength = pathlen, + .lpszUserName = user, + .dwUserNameLength = userlen, + .lpszPassword = pass, + .dwPasswordLength = passlen, + .lpszExtraInfo = extra, + .dwExtraInfoLength = countof(extra), + }; + + if (!MultiByteToWideChar(CP_THREAD_ACP, 0, uri, -1, wuri, countof(wuri))) + { + DBG1(DBG_LIB, "converting URI failed: %u", GetLastError()); + return FALSE; + } + if (!WinHttpCrackUrl(wuri, 0, ICU_ESCAPE, &comps)) + { + DBG1(DBG_LIB, "cracking URI failed: %u", GetLastError()); + return FALSE; + } + if (comps.nScheme == INTERNET_SCHEME_HTTPS) + { + *flags |= WINHTTP_FLAG_SECURE; + } + if (comps.dwExtraInfoLength) + { + wcsncat(path, extra, countof(path) - comps.dwUrlPathLength - 1); + } + if (comps.nPort) + { + *port = comps.nPort; + } + return TRUE; +} + +/** + * Set credentials for basic authentication, if given + */ +static bool set_credentials(private_winhttp_fetcher_t *this, + HINTERNET *request, LPWSTR user, LPWSTR pass) +{ + if (!wcslen(user) && !wcslen(pass)) + { /* skip */ + return TRUE; + } + return WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER, + WINHTTP_AUTH_SCHEME_BASIC, user, pass, NULL); +} + +METHOD(fetcher_t, fetch, status_t, + private_winhttp_fetcher_t *this, char *uri, void *userdata) +{ + INTERNET_PORT port = INTERNET_DEFAULT_PORT; + status_t status = FAILED; + DWORD flags = 0; + HINTERNET connection, request; + WCHAR host[256], path[512], user[256], pass[256], *method; + + if (this->request.len) + { + method = L"POST"; + } + else + { + method = L"GET"; + } + + if (this->result) + { /* zero-initialize for early failures */ + *this->result = 0; + } + + if (parse_uri(this, uri, host, countof(host), path, countof(path), + user, countof(user), pass, countof(pass), &flags, &port)) + { + connection = WinHttpConnect(this->session, host, port, 0); + if (connection) + { + request = WinHttpOpenRequest(connection, method, path, this->version, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, flags); + if (request) + { + if (set_credentials(this, request, user, pass) && + send_request(this, request) && + read_result(this, request, userdata)) + { + status = SUCCESS; + } + WinHttpCloseHandle(request); + } + else + { + DBG1(DBG_LIB, "opening request failed: %u", GetLastError()); + } + WinHttpCloseHandle(connect); + } + else + { + DBG1(DBG_LIB, "connection failed: %u", GetLastError()); + } + } + return status; +} + +/** + * Append an header as wide string + */ +static bool append_header(private_winhttp_fetcher_t *this, char *name) +{ + int len; + LPWSTR buf; + + len = MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, NULL, 0); + if (!len) + { + return FALSE; + } + buf = calloc(len, sizeof(WCHAR)); + if (!MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, buf, len)) + { + free(buf); + return FALSE; + } + this->headers->insert_last(this->headers, buf); + return TRUE; +} + +METHOD(fetcher_t, set_option, bool, + private_winhttp_fetcher_t *this, fetcher_option_t option, ...) +{ + bool supported = TRUE; + char buf[128]; + va_list args; + + va_start(args, option); + switch (option) + { + case FETCH_REQUEST_DATA: + this->request = va_arg(args, chunk_t); + break; + case FETCH_REQUEST_TYPE: + snprintf(buf, sizeof(buf), "Content-Type: %s", va_arg(args, char*)); + supported = append_header(this, buf); + break; + case FETCH_REQUEST_HEADER: + supported = append_header(this, va_arg(args, char*)); + break; + case FETCH_HTTP_VERSION_1_0: + this->version = L"HTTP/1.0"; + break; + case FETCH_TIMEOUT: + this->timeout = va_arg(args, u_int) * 1000; + break; + case FETCH_CALLBACK: + this->cb = va_arg(args, fetcher_callback_t); + break; + case FETCH_RESPONSE_CODE: + this->result = va_arg(args, u_int*); + break; + case FETCH_SOURCEIP: + /* not supported, FALL */ + default: + supported = FALSE; + break; + } + va_end(args); + return supported; +} + +METHOD(fetcher_t, destroy, void, + private_winhttp_fetcher_t *this) +{ + WinHttpCloseHandle(this->session); + this->headers->destroy_function(this->headers, free); + free(this); +} +/* + * Described in header. + */ +winhttp_fetcher_t *winhttp_fetcher_create() +{ + private_winhttp_fetcher_t *this; + + INIT(this, + .public = { + .interface = { + .fetch = _fetch, + .set_option = _set_option, + .destroy = _destroy, + }, + }, + .version = L"HTTP/1.1", + .cb = fetcher_default_callback, + .headers = linked_list_create(), + .session = WinHttpOpen(L"strongSwan WinHTTP fetcher", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0), + ); + + if (!this->session) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.h b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.h new file mode 100644 index 000000000..6129eb889 --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +/** + * @defgroup winhttp_fetcher winhttp_fetcher + * @{ @ingroup winhttp_p + */ + +#ifndef WINHTTP_FETCHER_H_ +#define WINHTTP_FETCHER_H_ + +#include <library.h> + +typedef struct winhttp_fetcher_t winhttp_fetcher_t; + +/** + * Fetcher implementation using Microsofts WinHTTP. + */ +struct winhttp_fetcher_t { + + /** + * Implements fetcher interface. + */ + fetcher_t interface; +}; + +/** + * Create a winhttp_fetcher instance + * + * @return WinHTTP based fetcher + */ +winhttp_fetcher_t *winhttp_fetcher_create(); + +#endif /** WINHTTP_FETCHER_H_ @}*/ diff --git a/src/libstrongswan/plugins/winhttp/winhttp_plugin.c b/src/libstrongswan/plugins/winhttp/winhttp_plugin.c new file mode 100644 index 000000000..8b67ff58b --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_plugin.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +#include "winhttp_plugin.h" +#include "winhttp_fetcher.h" + +typedef struct private_winhttp_plugin_t private_winhttp_plugin_t; + +/** + * Private data of winhttp_plugin + */ +struct private_winhttp_plugin_t { + + /** + * Public functions + */ + winhttp_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_winhttp_plugin_t *this) +{ + return "winhttp"; +} + +METHOD(plugin_t, get_features, int, + private_winhttp_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(FETCHER, winhttp_fetcher_create), + PLUGIN_PROVIDE(FETCHER, "http://"), + PLUGIN_PROVIDE(FETCHER, "https://"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_winhttp_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *winhttp_plugin_create() +{ + private_winhttp_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/winhttp/winhttp_plugin.h b/src/libstrongswan/plugins/winhttp/winhttp_plugin.h new file mode 100644 index 000000000..30cd0518a --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +/** + * @defgroup winhttp_p winhttp + * @ingroup plugins + * + * @defgroup winhttp_plugin winhttp_plugin + * @{ @ingroup winhttp_p + */ + +#ifndef WINHTTP_PLUGIN_H_ +#define WINHTTP_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct winhttp_plugin_t winhttp_plugin_t; + +/** + * Plugin implementing fetcher interface using Microsofts WinHTTP. + */ +struct winhttp_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** WINHTTP_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/tests/suites/test_fetch_http.c b/src/libstrongswan/tests/suites/test_fetch_http.c index 42743c787..9f1eef2f3 100644 --- a/src/libstrongswan/tests/suites/test_fetch_http.c +++ b/src/libstrongswan/tests/suites/test_fetch_http.c @@ -281,7 +281,7 @@ START_TEST(test_response_code) { stream_service_t *service; status_t status; - chunk_t data; + chunk_t data = chunk_empty; char uri[256]; u_int code; diff --git a/src/pki/commands/verify.c b/src/pki/commands/verify.c index 6cfaaf263..8cc633a95 100644 --- a/src/pki/commands/verify.c +++ b/src/pki/commands/verify.c @@ -19,32 +19,53 @@ #include <credentials/certificates/certificate.h> #include <credentials/certificates/x509.h> +#include <credentials/sets/mem_cred.h> /** * Verify a certificate signature */ static int verify() { - certificate_t *cert, *ca; - char *file = NULL, *cafile = NULL; - bool good = FALSE; - char *arg; + bool trusted = FALSE, valid = FALSE, revoked = FALSE; + bool has_ca = FALSE, online = FALSE; + certificate_t *cert; + enumerator_t *enumerator; + auth_cfg_t *auth; + mem_cred_t *creds; + char *arg, *file = NULL; + + creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &creds->set); while (TRUE) { switch (command_getopt(&arg)) { case 'h': + creds->destroy(creds); return command_usage(NULL); case 'i': file = arg; continue; case 'c': - cafile = arg; + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, arg, BUILD_END); + if (!cert) + { + fprintf(stderr, "parsing CA certificate failed\n"); + goto end; + } + has_ca = TRUE; + creds->add_cert(creds, TRUE, cert); + continue; + case 'o': + online = TRUE; continue; case EOF: break; default: + creds->destroy(creds); return command_usage("invalid --verify option"); } break; @@ -63,7 +84,7 @@ static int verify() if (!chunk_from_fd(0, &chunk)) { fprintf(stderr, "reading certificate failed: %s\n", strerror(errno)); - return 1; + goto end; } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB, chunk, BUILD_END); @@ -72,60 +93,76 @@ static int verify() if (!cert) { fprintf(stderr, "parsing certificate failed\n"); - return 1; - } - if (cafile) - { - ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, cafile, BUILD_END); - if (!ca) - { - fprintf(stderr, "parsing CA certificate failed\n"); - return 1; - } + goto end; } - else - { - ca = cert; - } - if (cert->issued_by(cert, ca, NULL)) + creds->add_cert(creds, !has_ca, cert); + + enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr, + KEY_ANY, cert->get_subject(cert), online); + if (enumerator->enumerate(enumerator, &cert, &auth)) { + trusted = TRUE; if (cert->get_validity(cert, NULL, NULL, NULL)) { - if (cafile) - { - if (ca->get_validity(ca, NULL, NULL, NULL)) - { - printf("signature good, certificates valid\n"); - good = TRUE; - } - else - { - printf("signature good, CA certificates not valid now\n"); - } - } - else - { - printf("signature good, certificate valid\n"); - good = TRUE; - } + printf("certificate trusted, lifetimes valid"); + valid = TRUE; } else { - printf("certificate not valid now\n"); + printf("certificate trusted, but no valid lifetime"); } + if (online) + { + switch ((uintptr_t)auth->get(auth, AUTH_RULE_CRL_VALIDATION)) + { + case VALIDATION_GOOD: + printf(", certificate not revoked"); + break; + case VALIDATION_SKIPPED: + printf(", no revocation information"); + break; + case VALIDATION_STALE: + printf(", revocation information stale"); + break; + case VALIDATION_FAILED: + printf(", revocation checking failed"); + break; + case VALIDATION_ON_HOLD: + printf(", certificate revocation on hold"); + revoked = TRUE; + break; + case VALIDATION_REVOKED: + printf(", certificate revoked"); + revoked = TRUE; + break; + } + } + printf("\n"); } - else + enumerator->destroy(enumerator); + + if (!trusted) { - printf("signature invalid\n"); + printf("certificate untrusted\n"); } - if (cafile) + +end: + lib->credmgr->remove_set(lib->credmgr, &creds->set); + creds->destroy(creds); + + if (!trusted) { - ca->destroy(ca); + return 1; } - cert->destroy(cert); - - return good ? 0 : 2; + if (!valid) + { + return 2; + } + if (revoked) + { + return 3; + } + return 0; } /** @@ -140,7 +177,8 @@ static void __attribute__ ((constructor))reg() { {"help", 'h', 0, "show usage information"}, {"in", 'i', 1, "X.509 certificate to verify, default: stdin"}, - {"cacert", 'c', 1, "CA certificate, default: verify self signed"}, + {"cacert", 'c', 1, "CA certificate for trustchain verification"}, + {"online", 'o', 0, "enable online CRL/OCSP revocation checking"}, } }); } |