Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/wininet/http.c | 62 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-)
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index 56c995805b..220493718c 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -2083,6 +2083,38 @@ static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL uni } }
+static DWORD get_security_cert_struct(http_request_t *req, INTERNET_CERTIFICATE_INFOA *info) +{ + PCCERT_CONTEXT context; + DWORD len; + + context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn); + if(!context) + return ERROR_NOT_SUPPORTED; + + memset(info, 0, sizeof(*info)); + info->ftExpiry = context->pCertInfo->NotAfter; + info->ftStart = context->pCertInfo->NotBefore; + len = CertNameToStrA(context->dwCertEncodingType, + &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); + info->lpszSubjectInfo = LocalAlloc(0, len); + if(info->lpszSubjectInfo) + CertNameToStrA(context->dwCertEncodingType, + &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, + info->lpszSubjectInfo, len); + len = CertNameToStrA(context->dwCertEncodingType, + &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); + info->lpszIssuerInfo = LocalAlloc(0, len); + if(info->lpszIssuerInfo) + CertNameToStrA(context->dwCertEncodingType, + &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, + info->lpszIssuerInfo, len); + info->dwKeySize = NETCON_GetCipherStrength(req->netconn); + + CertFreeCertificateContext(context); + return ERROR_SUCCESS; +} + static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode) { http_request_t *req = (http_request_t*)hdr; @@ -2235,8 +2267,6 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe }
case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: { - PCCERT_CONTEXT context; - if(!req->netconn) return ERROR_INTERNET_INVALID_OPERATION;
@@ -2245,33 +2275,7 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe return ERROR_INSUFFICIENT_BUFFER; }
- context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn); - if(context) { - INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer; - DWORD len; - - memset(info, 0, sizeof(*info)); - info->ftExpiry = context->pCertInfo->NotAfter; - info->ftStart = context->pCertInfo->NotBefore; - len = CertNameToStrA(context->dwCertEncodingType, - &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); - info->lpszSubjectInfo = LocalAlloc(0, len); - if(info->lpszSubjectInfo) - CertNameToStrA(context->dwCertEncodingType, - &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, - info->lpszSubjectInfo, len); - len = CertNameToStrA(context->dwCertEncodingType, - &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); - info->lpszIssuerInfo = LocalAlloc(0, len); - if(info->lpszIssuerInfo) - CertNameToStrA(context->dwCertEncodingType, - &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, - info->lpszIssuerInfo, len); - info->dwKeySize = NETCON_GetCipherStrength(req->netconn); - CertFreeCertificateContext(context); - return ERROR_SUCCESS; - } - return ERROR_NOT_SUPPORTED; + return get_security_cert_struct(req, (INTERNET_CERTIFICATE_INFOA*)buffer); } case INTERNET_OPTION_CONNECT_TIMEOUT: if (*size < sizeof(DWORD))
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/wininet/http.c | 63 +++++++++++++++++++ dlls/wininet/resource.h | 3 + dlls/wininet/tests/http.c | 127 +++++++++++++++++++++++++++++++++++++- dlls/wininet/wininet.rc | 11 ++++ 4 files changed, 202 insertions(+), 2 deletions(-)
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index 220493718c..06e671e577 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -54,6 +54,7 @@
#include "internet.h" #include "zlib.h" +#include "resource.h" #include "wine/debug.h" #include "wine/exception.h"
@@ -2277,6 +2278,68 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
return get_security_cert_struct(req, (INTERNET_CERTIFICATE_INFOA*)buffer); } + case INTERNET_OPTION_SECURITY_CERTIFICATE: { + DWORD err; + int needed; + char fmt[512]; + char strength[16]; + char start_date[32]; + char start_time[32]; + char expiry_date[32]; + char expiry_time[32]; + SYSTEMTIME start, expiry; + INTERNET_CERTIFICATE_INFOA info; + + if(!size) + return ERROR_INVALID_PARAMETER; + + if(!req->netconn) { + *size = 0; + return ERROR_INTERNET_INVALID_OPERATION; + } + + if(!buffer) { + *size = 1; + return ERROR_INSUFFICIENT_BUFFER; + } + + if((err = get_security_cert_struct(req, &info))) + return err; + + if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH) + FIXME("INTERNET_OPTION_SECURITY_CERTIFICATE currently English-only\n"); + + LoadStringA(WININET_hModule, IDS_CERT_FORMAT, fmt, sizeof(fmt)); + LoadStringA(WININET_hModule, info.dwKeySize >= 128 ? IDS_CERT_HIGH : IDS_CERT_LOW, + strength, sizeof(strength)); + + FileTimeToSystemTime(&info.ftStart, &start); + FileTimeToSystemTime(&info.ftExpiry, &expiry); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time)); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time)); + + needed = _scprintf(fmt, info.lpszSubjectInfo, info.lpszIssuerInfo, + start_date, start_time, expiry_date, expiry_time, + strength, info.dwKeySize); + if(needed < *size) { + err = ERROR_SUCCESS; + *size = snprintf(buffer, *size, fmt, info.lpszSubjectInfo, info.lpszIssuerInfo, + start_date, start_time, expiry_date, expiry_time, + strength, info.dwKeySize); + }else { + err = ERROR_INSUFFICIENT_BUFFER; + *size = 1; + } + + LocalFree(info.lpszSubjectInfo); + LocalFree(info.lpszIssuerInfo); + LocalFree(info.lpszProtocolName); + LocalFree(info.lpszSignatureAlgName); + LocalFree(info.lpszEncryptionAlgName); + return err; + } case INTERNET_OPTION_CONNECT_TIMEOUT: if (*size < sizeof(DWORD)) return ERROR_INSUFFICIENT_BUFFER; diff --git a/dlls/wininet/resource.h b/dlls/wininet/resource.h index 256a374af0..190d3cc397 100644 --- a/dlls/wininet/resource.h +++ b/dlls/wininet/resource.h @@ -38,3 +38,6 @@ #define IDS_CERT_DATE_INVALID 0x502 #define IDS_CERT_CN_INVALID 0x503 #define IDS_CERT_ERRORS 0x504 +#define IDS_CERT_FORMAT 0x505 +#define IDS_CERT_HIGH 0x506 +#define IDS_CERT_LOW 0x507 diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c index c07d60d2a9..e0a38e05c4 100644 --- a/dlls/wininet/tests/http.c +++ b/dlls/wininet/tests/http.c @@ -6112,9 +6112,26 @@ static const cert_struct_test_t test_winehq_com_cert = { "webmaster@winehq.org" };
+static const char *cert_string_fmt = + "Subject:\r\n%s\r\n" + "Issuer:\r\n%s\r\n" + "Effective Date:\t%s %s\r\n" + "Expiration Date:\t%s %s\r\n" + "Security Protocol:\t%s\r\n" + "Signature Type:\t%s\r\n" + "Encryption Type:\t%s\r\n" + "Privacy Strength:\t%s (%u bits)"; + static void test_cert_struct(HINTERNET req, const cert_struct_test_t *test) { INTERNET_CERTIFICATE_INFOA info; + SYSTEMTIME start, expiry; + char expiry_date[32]; + char expiry_time[32]; + char start_date[32]; + char start_time[32]; + char expect[512]; + char actual[512]; DWORD size; BOOL res;
@@ -6138,6 +6155,42 @@ static void test_cert_struct(HINTERNET req, const cert_struct_test_t *test) ok(!info.lpszProtocolName, "lpszProtocolName = %s\n", info.lpszProtocolName); ok(info.dwKeySize >= 128 && info.dwKeySize <= 256, "dwKeySize = %u\n", info.dwKeySize);
+ if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH) + { + skip("Non-English locale (test with hardcoded English)\n"); + release_cert_info(&info); + return; + } + + size = sizeof(actual); + SetLastError(0xdeadbeef); + memset(actual, 0x55, sizeof(actual)); + res = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size); + ok(res, "InternetQueryOption failed: %u\n", GetLastError()); + + FileTimeToSystemTime(&info.ftStart, &start); + FileTimeToSystemTime(&info.ftExpiry, &expiry); + + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time)); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time)); + + snprintf(expect, sizeof(expect), cert_string_fmt, info.lpszSubjectInfo, info.lpszIssuerInfo, + start_date, start_time, expiry_date, expiry_time, + info.lpszSignatureAlgName, info.lpszEncryptionAlgName, info.lpszProtocolName, + info.dwKeySize >= 128 ? "High" : "Low", info.dwKeySize); + ok(size == strlen(actual), "size = %u\n", size); + ok(!strcmp(actual, expect), "cert = actual\n%s\n", actual); + + --size; + SetLastError(0xdeadbeef); + memset(actual, 0x55, sizeof(actual)); + res = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + ok(actual[0] == 0x55, "unexpected byte: %02x\n", actual[0]); + release_cert_info(&info); }
@@ -6217,7 +6270,7 @@ static void test_security_flags(void) INTERNET_CERTIFICATE_INFOA *cert; HINTERNET ses, conn, req; DWORD size, flags; - char buf[100]; + char buf[512]; BOOL res;
if (!https_support) @@ -6378,6 +6431,30 @@ static void test_security_flags(void) } HeapFree(GetProcessHeap(), 0, cert);
+ SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, NULL, NULL); + ok(!res && GetLastError() == ERROR_INVALID_PARAMETER, "InternetQueryOption failed: %d\n", GetLastError()); + + size = 0; + SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, NULL, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + + size = 42; + SetLastError(0xdeadbeef); + memset(buf, 0x55, sizeof(buf)); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, buf, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + ok(buf[0] == 0x55, "unexpected byte: %02x\n", buf[0]); + + size = sizeof(buf); + SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, buf, &size); + ok(res && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size < sizeof(buf), "unexpected size: %u\n", size); + CHECK_NOTIFIED2(INTERNET_STATUS_CONNECTING_TO_SERVER, 2); CHECK_NOTIFIED2(INTERNET_STATUS_CONNECTED_TO_SERVER, 2); CHECK_NOTIFIED2(INTERNET_STATUS_CLOSING_CONNECTION, 2); @@ -6562,9 +6639,10 @@ static void test_secure_connection(void) static const WCHAR get[] = {'G','E','T',0}; static const WCHAR testpage[] = {'/','t','e','s','t','s','/','h','e','l','l','o','.','h','t','m','l',0}; HINTERNET ses, con, req; - DWORD size, flags, err; + DWORD size, size2, flags, err; INTERNET_CERTIFICATE_INFOA *certificate_structA = NULL; INTERNET_CERTIFICATE_INFOW *certificate_structW = NULL; + char certstr1[512], certstr2[512]; BOOL ret;
ses = InternetOpenA("Gizmo5", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); @@ -6629,6 +6707,19 @@ static void test_secure_connection(void) } HeapFree(GetProcessHeap(), 0, certificate_structW);
+ SetLastError(0xdeadbeef); + size = sizeof(certstr1); + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, certstr1, &size); + ok(ret && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + size2 = sizeof(certstr2); + ret = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, certstr2, &size2); + ok(ret && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + + ok(size == size2, "expected same size\n"); + ok(!strcmp(certstr1, certstr2), "expected same string\n"); + InternetCloseHandle(req); InternetCloseHandle(con); InternetCloseHandle(ses); @@ -7517,6 +7608,37 @@ static void test_concurrent_header_access(void) CloseHandle( wait ); }
+static void test_cert_string(void) +{ + HINTERNET ses, con, req; + char actual[512]; + DWORD size; + BOOL res; + + ses = InternetOpenA( "winetest", 0, NULL, NULL, 0 ); + ok( ses != NULL, "InternetOpenA failed\n" ); + + con = InternetConnectA( ses, "test.winehq.org", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, + INTERNET_SERVICE_HTTP, 0, 0 ); + ok( con != NULL, "InternetConnectA failed %u\n", GetLastError() ); + + req = HttpOpenRequestA( con, NULL, "/", NULL, NULL, NULL, 0, 0 ); + ok( req != NULL, "HttpOpenRequestA failed %u\n", GetLastError() ); + + size = sizeof(actual); + SetLastError( 0xdeadbeef ); + memset( actual, 0x55, sizeof(actual) ); + res = InternetQueryOptionA( req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size ); + ok( !res && GetLastError() == ERROR_INTERNET_INVALID_OPERATION, + "InternetQueryOption failed: %u\n", GetLastError() ); + ok( size == 0, "unexpected size: %u\n", size ); + ok( actual[0] == 0x55, "unexpected byte: %02x\n", actual[0] ); + + InternetCloseHandle( req ); + InternetCloseHandle( con ); + InternetCloseHandle( ses ); +} + START_TEST(http) { HMODULE hdll; @@ -7566,5 +7688,6 @@ START_TEST(http) test_connection_failure(); test_default_service_port(); test_concurrent_header_access(); + test_cert_string(); free_events(); } diff --git a/dlls/wininet/wininet.rc b/dlls/wininet/wininet.rc index b6e35629ca..5bd5b48ccc 100644 --- a/dlls/wininet/wininet.rc +++ b/dlls/wininet/wininet.rc @@ -29,6 +29,17 @@ STRINGTABLE IDS_CERT_DATE_INVALID "The date on the certificate is invalid." IDS_CERT_CN_INVALID "The name on the certificate does not match the site." IDS_CERT_ERRORS "There is at least one unspecified security problem with this certificate." + + IDS_CERT_FORMAT "Subject:\015\n%s\015\n" \ + "Issuer:\015\n%s\015\n" \ + "Effective Date:\t%s %s\015\n" \ + "Expiration Date:\t%s %s\015\n" \ + "Security Protocol:\t(null)\015\n" \ + "Signature Type:\t(null)\015\n" \ + "Encryption Type:\t(null)\015\n" \ + "Privacy Strength:\t%s (%u bits)" + IDS_CERT_HIGH "High" + IDS_CERT_LOW "Low" }
IDD_PROXYDLG DIALOG 36, 24, 220, 146
Signed-off-by: Jacek Caban jacek@codeweavers.com
Hi Daniel,
On 03.08.2020 01:23, Daniel Lehman wrote:
if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
FIXME("INTERNET_OPTION_SECURITY_CERTIFICATE currently English-only\n");
I sent the sign off too soon. Is this FIXME accurate? It seems that you added everything that's needed for other languages in the code itself. Once translators translate resources, it will be supported, which is not different than any other locale-dependent place.
Thanks,
Jacek
I sent the sign off too soon. Is this FIXME accurate? It seems that you added everything that's needed for other languages in the code itself. Once translators translate resources, it will be supported, which is not different than any other locale-dependent place.
ah... no. i originally had hardcoded english, then made it translatable. forgot to remove it when squashing. i'll submit with it removed. ok to just resend the patch 0002?
thanks daniel
On 03.08.2020 19:18, Daniel Lehman wrote:
I sent the sign off too soon. Is this FIXME accurate? It seems that you added everything that's needed for other languages in the code itself. Once translators translate resources, it will be supported, which is not different than any other locale-dependent place.
ah... no. i originally had hardcoded english, then made it translatable. forgot to remove it when squashing. i'll submit with it removed. ok to just resend the patch 0002?
Sure, it should be fine.
Thanks,
Jacek
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=76418
Your paranoid android.
=== debiant (32 bit Chinese:China report) ===
wininet: http.c:5035: Test failed: expected 1 pending read, got 2