[PATCH] wininet: Implement INTERNET_OPTION_SECURITY_CERTIFICATE flag for InternetQueryOption.

Jacek Caban jacek at codeweavers.com
Thu Jul 30 08:17:49 CDT 2020


Hi Daniel,

Sorry for the delay.

On 26.07.2020 03:21, Daniel Lehman wrote:
> Signed-off-by: Daniel Lehman <dlehman25 at gmail.com>
> ---
>   dlls/wininet/http.c       | 120 ++++++++++++++++++++++++++++++++++++++
>   dlls/wininet/resource.h   |   3 +
>   dlls/wininet/tests/http.c | 112 ++++++++++++++++++++++++++++++++++-
>   dlls/wininet/wininet.rc   |  12 ++++
>   4 files changed, 245 insertions(+), 2 deletions(-)
>
> diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
> index 56c995805b..379455fddb 100644
> --- a/dlls/wininet/http.c
> +++ b/dlls/wininet/http.c
> @@ -35,6 +35,7 @@
>   #include <stdarg.h>
>   #include <stdio.h>
>   #include <time.h>
> +#include <math.h>
>   #include <assert.h>
>   #include <errno.h>
>   #include <limits.h>
> @@ -54,6 +55,7 @@
>   
>   #include "internet.h"
>   #include "zlib.h"
> +#include "resource.h"
>   #include "wine/debug.h"
>   #include "wine/exception.h"
>   
> @@ -2273,6 +2275,124 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
>           }
>           return ERROR_NOT_SUPPORTED;
>       }
> +    case INTERNET_OPTION_SECURITY_CERTIFICATE: {
> +        char fmt[256];
> +        const char nullA[] = "(null)"; /* always appears in English */
> +        CERT_CONTEXT *context;
> +        char *subject = NULL;
> +        char *issuer = NULL;
> +        char *start_date = NULL;
> +        char *start_time = NULL;
> +        char *expiry_date = NULL;
> +        char *expiry_time = NULL;
> +        char strength[16];
> +        int subject_len, issuer_len;
> +        int start_date_len, start_time_len;
> +        int expiry_date_len, expiry_time_len;
> +        SYSTEMTIME start, expiry;
> +        DWORD needed, keysize;
> +
> +        if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
> +            FIXME("INTERNET_OPTION_SECURITY_CERTIFICATE currently English-only\n");
> +
> +        if(!req->netconn)
> +            return ERROR_INTERNET_INVALID_OPERATION;
> +
> +        if(!size)
> +            return ERROR_INVALID_PARAMETER;
> +
> +        if(!buffer) {
> +            *size = 1;
> +            return ERROR_INSUFFICIENT_BUFFER;
> +        }
> +
> +        context = (CERT_CONTEXT *)NETCON_GetCert(req->netconn);
> +        if(!context)(Decoding strings directly to return buffer would be another option).
> +            return ERROR_NOT_SUPPORTED;
> +
> +        needed = LoadStringA(WININET_hModule, IDS_CERT_FORMAT, fmt, sizeof(fmt));
> +        needed += 1 - 20 * 2 + 9; /* include room for \0, subtract 20 format specifiers, add 9 \r */
> +        if(needed > *size) goto error;
> +
> +        subject_len = CertNameToStrA(context->dwCertEncodingType, &context->pCertInfo->Subject,
> +                                     CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
> +        needed += subject_len - 1; /* minus \0 */
> +        if(needed > *size) goto error;
> +
> +        issuer_len = CertNameToStrA(context->dwCertEncodingType, &context->pCertInfo->Issuer,
> +                                    CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
> +        needed += issuer_len - 1;
> +        if(needed > *size) goto error;
> +
> +        FileTimeToSystemTime(&context->pCertInfo->NotBefore, &start);
> +        start_date_len = GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, NULL, 0);
> +        start_time_len = GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, NULL, 0);
> +        needed += start_date_len + start_time_len - 2;
> +        if(needed > *size) goto error;
> +
> +        FileTimeToSystemTime(&context->pCertInfo->NotAfter, &expiry);
> +        expiry_date_len = GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, NULL, 0);
> +        expiry_time_len = GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, NULL, 0);
> +        needed += expiry_date_len + expiry_time_len - 2;
> +        if(needed > *size) goto error;
> +
> +        needed += sizeof(nullA) * 3 - 3; /* protocol, signature type, encryption type */
> +        if(needed > *size) goto error;
> +
> +        keysize = NETCON_GetCipherStrength(req->netconn);
> +        needed += keysize ? floor(log10(keysize))+1 : 1;
> +        needed += LoadStringA(WININET_hModule, keysize >= 128 ? IDS_CERT_HIGH : IDS_CERT_LOW,
> +                              strength, sizeof(strength));
> +        if(needed > *size) goto error;
> +
> +        if(!(subject = heap_alloc(subject_len)) ||
> +           !(issuer = heap_alloc(issuer_len)) ||
> +           !(start_date = heap_alloc(start_date_len)) ||
> +           !(start_time = heap_alloc(start_time_len)) ||
> +           !(expiry_date = heap_alloc(expiry_date_len)) ||
> +           !(expiry_time = heap_alloc(expiry_time_len)))
> +            goto error;


If you're using dynamic allocation anyway, you could as well move 
INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT to a helper and operate on 
INTERNET_CERTIFICATE_INFO here, maybe even combined with _cprintf for 
size check. It should simplify things a bit. Date and time strings 
should be fine with buffers on a stack. (Decoding strings directly to 
return buffer would be another option, but it's necessarily worth the 
complication).


> diff --git a/dlls/wininet/wininet.rc b/dlls/wininet/wininet.rc
> index b6e35629ca..7e2830e7ff 100644
> --- a/dlls/wininet/wininet.rc
> +++ b/dlls/wininet/wininet.rc
> @@ -29,6 +29,18 @@ 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."
> +
> +  /* each %c is a \r */
> +  IDS_CERT_FORMAT       "Subject:%c\n%s%c\n" \
> +                        "Issuer:%c\n%s%c\n" \
> +                        "Effective Date:\t%s %s%c\n" \
> +                        "Expiration Date:\t%s %s%c\n" \
> +                        "Security Protocol:\t%s%c\n" \
> +                        "Signature Type:\t%s%c\n" \
> +                        "Encryption Type:\t%s%c\n" \
> +                        "Privacy Strength:\t%s (%u bits)"
> +  IDS_CERT_HIGH         "High"
> +  IDS_CERT_LOW          "Low"


You could use \015 as \r replacement instead of %c trick.


Thanks,

Jacek




More information about the wine-devel mailing list