From 6f90fc806104328ec62bfa93893c2c79d5bda5eb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 30 Jan 2014 14:07:53 +0100 Subject: [PATCH 1/6] winhttp: Implement a http(s) fetcher based on Microsofts WinHTTP API --- configure.ac | 4 + src/libstrongswan/Makefile.am | 7 + src/libstrongswan/plugins/winhttp/Makefile.am | 18 + .../plugins/winhttp/winhttp_fetcher.c | 342 ++++++++++++++++++ .../plugins/winhttp/winhttp_fetcher.h | 46 +++ .../plugins/winhttp/winhttp_plugin.c | 74 ++++ .../plugins/winhttp/winhttp_plugin.h | 42 +++ 7 files changed, 533 insertions(+) create mode 100644 src/libstrongswan/plugins/winhttp/Makefile.am create mode 100644 src/libstrongswan/plugins/winhttp/winhttp_fetcher.c create mode 100644 src/libstrongswan/plugins/winhttp/winhttp_fetcher.h create mode 100644 src/libstrongswan/plugins/winhttp/winhttp_plugin.c create mode 100644 src/libstrongswan/plugins/winhttp/winhttp_plugin.h diff --git a/configure.ac b/configure.ac index 736f097c1a..ec6ec9bb3e 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.]) @@ -1160,6 +1161,7 @@ t_plugins= ADD_PLUGIN([test-vectors], [s charon scepclient pki]) ADD_PLUGIN([curl], [s charon scepclient scripts nm cmd]) +ADD_PLUGIN([winhttp], [s charon scripts]) ADD_PLUGIN([soup], [s charon scripts nm cmd]) ADD_PLUGIN([unbound], [s charon scripts]) ADD_PLUGIN([ldap], [s charon scepclient scripts nm cmd]) @@ -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/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 2602a9ebaf..9a08825fd7 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 0000000000..f6b00a71e9 --- /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 0000000000..600fa96588 --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -0,0 +1,342 @@ +/* + * 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 . + * + * 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 +#include +#include + +#include "winhttp_fetcher.h" + +#include + +/** + * 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; +}; + +/** + * 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]; + + if (!WinHttpReceiveResponse(request, NULL)) + { + DBG1(DBG_LIB, "reading HTTP response header failed: %u", GetLastError()); + 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, + 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, + .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; +} + +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], *method; + + if (this->request.len) + { + method = L"POST"; + } + else + { + method = L"GET"; + } + + if (parse_uri(this, uri, host, countof(host), path, countof(path), + &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 (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_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 0000000000..6129eb889c --- /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 . + * + * 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 + +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 0000000000..8b67ff58be --- /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 . + * + * 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 0000000000..30cd0518a0 --- /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 . + * + * 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 + +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_ @}*/ From 2e0816e1df4b70ac6fe81cc79d610d85ebfe9a34 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 19 May 2014 16:15:37 +0200 Subject: [PATCH 2/6] winhttp: Support new response code fetcher option --- .../plugins/winhttp/winhttp_fetcher.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c index 600fa96588..9119e1d0a9 100644 --- a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -72,6 +72,11 @@ struct private_winhttp_fetcher_t { * Timeout for operations, in ms */ u_long timeout; + + /** + * User pointer to store HTTP status code to + */ + u_int *result; }; /** @@ -115,12 +120,33 @@ static bool read_result(private_winhttp_fetcher_t *this, HINTERNET request, { 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; @@ -207,6 +233,11 @@ METHOD(fetcher_t, fetch, status_t, 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), &flags, &port)) { @@ -291,6 +322,9 @@ METHOD(fetcher_t, set_option, bool, 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: From 8a072fc50ac76af682e9fd0e21a31cf8411b9838 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 2 Jun 2014 11:55:18 +0200 Subject: [PATCH 3/6] winhttp: Support basic authentication for URLs having credentials --- .../plugins/winhttp/winhttp_fetcher.c | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c index 9119e1d0a9..4a822bd9e4 100644 --- a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -177,6 +177,7 @@ static bool read_result(private_winhttp_fetcher_t *this, HINTERNET request, */ 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]; @@ -186,6 +187,10 @@ static bool parse_uri(private_winhttp_fetcher_t *this, char *uri, .dwHostNameLength = hostlen, .lpszUrlPath = path, .dwUrlPathLength = pathlen, + .lpszUserName = user, + .dwUserNameLength = userlen, + .lpszPassword = pass, + .dwPasswordLength = passlen, .lpszExtraInfo = extra, .dwExtraInfoLength = countof(extra), }; @@ -215,6 +220,20 @@ static bool parse_uri(private_winhttp_fetcher_t *this, char *uri, 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) { @@ -222,7 +241,7 @@ METHOD(fetcher_t, fetch, status_t, status_t status = FAILED; DWORD flags = 0; HINTERNET connection, request; - WCHAR host[256], path[512], *method; + WCHAR host[256], path[512], user[256], pass[256], *method; if (this->request.len) { @@ -239,7 +258,7 @@ METHOD(fetcher_t, fetch, status_t, } if (parse_uri(this, uri, host, countof(host), path, countof(path), - &flags, &port)) + user, countof(user), pass, countof(pass), &flags, &port)) { connection = WinHttpConnect(this->session, host, port, 0); if (connection) @@ -249,7 +268,8 @@ METHOD(fetcher_t, fetch, status_t, WINHTTP_DEFAULT_ACCEPT_TYPES, flags); if (request) { - if (send_request(this, request) && + if (set_credentials(this, request, user, pass) && + send_request(this, request) && read_result(this, request, userdata)) { status = SUCCESS; From 74eedc8061fd2994be1ff3460c54f8cb65273b43 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 2 Jun 2014 12:52:32 +0200 Subject: [PATCH 4/6] unit-tests: Zero-initialize chunk to avoid free on non-successful fetch If the fetch fails, the fetcher is not required to return an empty chunk. Avoid the resulting invalid free() by initializing data.ptr to NULL. --- src/libstrongswan/tests/suites/test_fetch_http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/suites/test_fetch_http.c b/src/libstrongswan/tests/suites/test_fetch_http.c index 42743c7872..9f1eef2f37 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; From f48c26bce314684a11fbe5b4d9124a8ed3997c2a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 4 Apr 2014 10:37:59 +0200 Subject: [PATCH 5/6] pki: Support complex trustchain and revocation checking in --verify --- configure.ac | 8 +-- src/pki/commands/verify.c | 140 ++++++++++++++++++++++++-------------- 2 files changed, 93 insertions(+), 55 deletions(-) diff --git a/configure.ac b/configure.ac index ec6ec9bb3e..87aa40dd94 100644 --- a/configure.ac +++ b/configure.ac @@ -1160,9 +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([winhttp], [s charon scripts]) -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]) @@ -1180,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]) diff --git a/src/pki/commands/verify.c b/src/pki/commands/verify.c index 6cfaaf263e..8cc633a950 100644 --- a/src/pki/commands/verify.c +++ b/src/pki/commands/verify.c @@ -19,32 +19,53 @@ #include #include +#include /** * 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; + goto end; } - 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; - } - } - 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 - { - printf("signature invalid\n"); - } - if (cafile) - { - ca->destroy(ca); - } - cert->destroy(cert); + enumerator->destroy(enumerator); - return good ? 0 : 2; + if (!trusted) + { + printf("certificate untrusted\n"); + } + +end: + lib->credmgr->remove_set(lib->credmgr, &creds->set); + creds->destroy(creds); + + if (!trusted) + { + return 1; + } + 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"}, } }); } From c572401b3474cc003bcbc202921e97f7f67adaeb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 2 Apr 2014 11:36:19 +0200 Subject: [PATCH 6/6] travis: Build "all" tests without Windows HTTP fetcher We don't include it in the Windows build test either, as MinGW does not come with -lwinhttp. --- scripts/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test.sh b/scripts/test.sh index 103b411031..7c1dc89ac6 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"