mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
Implement --pinnedpubkey option to pin public keys
This commit is contained in:
parent
598445ebd1
commit
ade891448c
@ -1776,6 +1776,18 @@ system-specified locations, chosen at OpenSSL installation time.
|
|||||||
Specifies a CRL file in @var{file}. This is needed for certificates
|
Specifies a CRL file in @var{file}. This is needed for certificates
|
||||||
that have been revocated by the CAs.
|
that have been revocated by the CAs.
|
||||||
|
|
||||||
|
@cindex SSL Public Key Pin
|
||||||
|
@item --pinnedpubkey=file/hashes
|
||||||
|
Tells wget to use the specified public key file (or hashes) to verify the peer.
|
||||||
|
This can be a path to a file which contains a single public key in PEM or DER
|
||||||
|
format, or any number of base64 encoded sha256 hashes preceded by ``sha256//''
|
||||||
|
and separated by ``;''
|
||||||
|
|
||||||
|
When negotiating a TLS or SSL connection, the server sends a certificate
|
||||||
|
indicating its identity. A public key is extracted from this certificate and if
|
||||||
|
it does not exactly match the public key(s) provided to this option, wget will
|
||||||
|
abort the connection before sending or receiving any data.
|
||||||
|
|
||||||
@cindex entropy, specifying source of
|
@cindex entropy, specifying source of
|
||||||
@cindex randomness, specifying source of
|
@cindex randomness, specifying source of
|
||||||
@item --random-file=@var{file}
|
@item --random-file=@var{file}
|
||||||
|
67
src/gnutls.c
67
src/gnutls.c
@ -37,6 +37,7 @@ as that of the covered work. */
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <gnutls/abstract.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include <gnutls/x509.h>
|
#include <gnutls/x509.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@ -671,6 +672,59 @@ ssl_connect_wget (int fd, const char *hostname, int *continue_session)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
pkp_pin_peer_pubkey (gnutls_x509_crt_t cert, const char *pinnedpubkey)
|
||||||
|
{
|
||||||
|
/* Scratch */
|
||||||
|
size_t len1 = 0, len2 = 0;
|
||||||
|
char *buff1 = NULL;
|
||||||
|
|
||||||
|
gnutls_pubkey_t key = NULL;
|
||||||
|
|
||||||
|
/* Result is returned to caller */
|
||||||
|
int ret = 0;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
/* if a path wasn't specified, don't pin */
|
||||||
|
if (NULL == pinnedpubkey)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (NULL == cert)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* Begin Gyrations to get the public key */
|
||||||
|
gnutls_pubkey_init (&key);
|
||||||
|
|
||||||
|
ret = gnutls_pubkey_import_x509 (key, cert, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
ret = gnutls_pubkey_export (key, GNUTLS_X509_FMT_DER, NULL, &len1);
|
||||||
|
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0)
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
buff1 = xmalloc (len1);
|
||||||
|
|
||||||
|
len2 = len1;
|
||||||
|
|
||||||
|
ret = gnutls_pubkey_export (key, GNUTLS_X509_FMT_DER, buff1, &len2);
|
||||||
|
if (ret < 0 || len1 != len2)
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
/* End Gyrations */
|
||||||
|
|
||||||
|
/* The one good exit point */
|
||||||
|
result = wg_pin_peer_pubkey (pinnedpubkey, buff1, len1);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (NULL != key)
|
||||||
|
gnutls_pubkey_deinit (key);
|
||||||
|
|
||||||
|
xfree (buff1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#define _CHECK_CERT(flag,msg) \
|
#define _CHECK_CERT(flag,msg) \
|
||||||
if (status & (flag))\
|
if (status & (flag))\
|
||||||
{\
|
{\
|
||||||
@ -691,9 +745,10 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
him about problems with the server's certificate. */
|
him about problems with the server's certificate. */
|
||||||
const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
|
const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
bool pinsuccess = opt.pinnedpubkey == NULL;
|
||||||
|
|
||||||
/* The user explicitly said to not check for the certificate. */
|
/* The user explicitly said to not check for the certificate. */
|
||||||
if (opt.check_cert == CHECK_CERT_QUIET)
|
if (opt.check_cert == CHECK_CERT_QUIET && pinsuccess)
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
err = gnutls_certificate_verify_peers2 (ctx->session, &status);
|
err = gnutls_certificate_verify_peers2 (ctx->session, &status);
|
||||||
@ -760,6 +815,13 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
quote (host));
|
quote (host));
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pinsuccess = pkp_pin_peer_pubkey (cert, opt.pinnedpubkey);
|
||||||
|
if (!pinsuccess)
|
||||||
|
{
|
||||||
|
logprintf (LOG_ALWAYS, _("The public key does not match pinned public key!\n"));
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
crt_deinit:
|
crt_deinit:
|
||||||
gnutls_x509_crt_deinit (cert);
|
gnutls_x509_crt_deinit (cert);
|
||||||
}
|
}
|
||||||
@ -770,5 +832,6 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return opt.check_cert == CHECK_CERT_ON ? success : true;
|
/* never return true if pinsuccess fails */
|
||||||
|
return !pinsuccess ? false : (opt.check_cert == CHECK_CERT_ON ? success : true);
|
||||||
}
|
}
|
||||||
|
@ -254,6 +254,9 @@ static const struct {
|
|||||||
{ "passiveftp", &opt.ftp_pasv, cmd_boolean },
|
{ "passiveftp", &opt.ftp_pasv, cmd_boolean },
|
||||||
{ "passwd", &opt.ftp_passwd, cmd_string },/* deprecated*/
|
{ "passwd", &opt.ftp_passwd, cmd_string },/* deprecated*/
|
||||||
{ "password", &opt.passwd, cmd_string },
|
{ "password", &opt.passwd, cmd_string },
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
{ "pinnedpubkey", &opt.pinnedpubkey, cmd_string },
|
||||||
|
#endif
|
||||||
{ "postdata", &opt.post_data, cmd_string },
|
{ "postdata", &opt.post_data, cmd_string },
|
||||||
{ "postfile", &opt.post_file_name, cmd_file },
|
{ "postfile", &opt.post_file_name, cmd_file },
|
||||||
{ "preferfamily", NULL, cmd_spec_prefer_family },
|
{ "preferfamily", NULL, cmd_spec_prefer_family },
|
||||||
|
@ -350,6 +350,7 @@ static struct cmdline_option option_data[] =
|
|||||||
{ "parent", 0, OPT__PARENT, NULL, optional_argument },
|
{ "parent", 0, OPT__PARENT, NULL, optional_argument },
|
||||||
{ "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 },
|
{ "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 },
|
||||||
{ "password", 0, OPT_VALUE, "password", -1 },
|
{ "password", 0, OPT_VALUE, "password", -1 },
|
||||||
|
{ IF_SSL ("pinnedpubkey"), 0, OPT_VALUE, "pinnedpubkey", -1 },
|
||||||
{ "post-data", 0, OPT_VALUE, "postdata", -1 },
|
{ "post-data", 0, OPT_VALUE, "postdata", -1 },
|
||||||
{ "post-file", 0, OPT_VALUE, "postfile", -1 },
|
{ "post-file", 0, OPT_VALUE, "postfile", -1 },
|
||||||
{ "prefer-family", 0, OPT_VALUE, "preferfamily", -1 },
|
{ "prefer-family", 0, OPT_VALUE, "preferfamily", -1 },
|
||||||
@ -784,6 +785,11 @@ HTTPS (SSL/TLS) options:\n"),
|
|||||||
--ca-directory=DIR directory where hash list of CAs is stored\n"),
|
--ca-directory=DIR directory where hash list of CAs is stored\n"),
|
||||||
N_("\
|
N_("\
|
||||||
--crl-file=FILE file with bundle of CRLs\n"),
|
--crl-file=FILE file with bundle of CRLs\n"),
|
||||||
|
N_("\
|
||||||
|
--pinnedpubkey=FILE/HASHES Public key (PEM/DER) file, or any number\n\
|
||||||
|
of base64 encoded sha256 hashes preceded by\n\
|
||||||
|
\'sha256//\' and seperated by \';\', to verify\n\
|
||||||
|
peer against\n"),
|
||||||
#if defined(HAVE_LIBSSL) || defined(HAVE_LIBSSL32)
|
#if defined(HAVE_LIBSSL) || defined(HAVE_LIBSSL32)
|
||||||
N_("\
|
N_("\
|
||||||
--random-file=FILE file with random data for seeding the SSL PRNG\n"),
|
--random-file=FILE file with random data for seeding the SSL PRNG\n"),
|
||||||
|
@ -650,6 +650,65 @@ static char *_get_rfc2253_formatted (X509_NAME *name)
|
|||||||
return out ? out : xstrdup("");
|
return out ? out : xstrdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Heavily modified from:
|
||||||
|
* https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
pkp_pin_peer_pubkey (X509* cert, const char *pinnedpubkey)
|
||||||
|
{
|
||||||
|
/* Scratch */
|
||||||
|
int len1 = 0, len2 = 0;
|
||||||
|
char *buff1 = NULL, *temp = NULL;
|
||||||
|
|
||||||
|
/* Result is returned to caller */
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
/* if a path wasn't specified, don't pin */
|
||||||
|
if (!pinnedpubkey)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!cert)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* Begin Gyrations to get the subjectPublicKeyInfo */
|
||||||
|
/* Thanks to Viktor Dukhovni on the OpenSSL mailing list */
|
||||||
|
|
||||||
|
/* https://groups.google.com/group/mailing.openssl.users/browse_thread
|
||||||
|
/thread/d61858dae102c6c7 */
|
||||||
|
len1 = i2d_X509_PUBKEY (X509_get_X509_PUBKEY (cert), NULL);
|
||||||
|
if (len1 < 1)
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
/* https://www.openssl.org/docs/crypto/buffer.html */
|
||||||
|
buff1 = temp = OPENSSL_malloc (len1);
|
||||||
|
if (!buff1)
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
/* https://www.openssl.org/docs/crypto/d2i_X509.html */
|
||||||
|
len2 = i2d_X509_PUBKEY (X509_get_X509_PUBKEY (cert), (unsigned char **) &temp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These checks are verifying we got back the same values as when we
|
||||||
|
* sized the buffer. It's pretty weak since they should always be the
|
||||||
|
* same. But it gives us something to test.
|
||||||
|
*/
|
||||||
|
if ((len1 != len2) || !temp || ((temp - buff1) != len1))
|
||||||
|
goto cleanup; /* failed */
|
||||||
|
|
||||||
|
/* End Gyrations */
|
||||||
|
|
||||||
|
/* The one good exit point */
|
||||||
|
result = wg_pin_peer_pubkey (pinnedpubkey, buff1, len1);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
/* https://www.openssl.org/docs/crypto/buffer.html */
|
||||||
|
if (NULL != buff1)
|
||||||
|
OPENSSL_free (buff1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Verify the validity of the certificate presented by the server.
|
/* Verify the validity of the certificate presented by the server.
|
||||||
Also check that the "common name" of the server, as presented by
|
Also check that the "common name" of the server, as presented by
|
||||||
its certificate, corresponds to HOST. (HOST typically comes from
|
its certificate, corresponds to HOST. (HOST typically comes from
|
||||||
@ -673,6 +732,7 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
long vresult;
|
long vresult;
|
||||||
bool success = true;
|
bool success = true;
|
||||||
bool alt_name_checked = false;
|
bool alt_name_checked = false;
|
||||||
|
bool pinsuccess = opt.pinnedpubkey == NULL;
|
||||||
|
|
||||||
/* If the user has specified --no-check-cert, we still want to warn
|
/* If the user has specified --no-check-cert, we still want to warn
|
||||||
him about problems with the server's certificate. */
|
him about problems with the server's certificate. */
|
||||||
@ -683,7 +743,7 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
assert (conn != NULL);
|
assert (conn != NULL);
|
||||||
|
|
||||||
/* The user explicitly said to not check for the certificate. */
|
/* The user explicitly said to not check for the certificate. */
|
||||||
if (opt.check_cert == CHECK_CERT_QUIET)
|
if (opt.check_cert == CHECK_CERT_QUIET && pinsuccess)
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
cert = SSL_get_peer_certificate (conn);
|
cert = SSL_get_peer_certificate (conn);
|
||||||
@ -877,6 +937,13 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pinsuccess = pkp_pin_peer_pubkey (cert, opt.pinnedpubkey);
|
||||||
|
if (!pinsuccess)
|
||||||
|
{
|
||||||
|
logprintf (LOG_ALWAYS, _("The public key does not match pinned public key!\n"));
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
DEBUGP (("X509 certificate successfully verified and matches host %s\n",
|
DEBUGP (("X509 certificate successfully verified and matches host %s\n",
|
||||||
@ -889,7 +956,8 @@ ssl_check_certificate (int fd, const char *host)
|
|||||||
To connect to %s insecurely, use `--no-check-certificate'.\n"),
|
To connect to %s insecurely, use `--no-check-certificate'.\n"),
|
||||||
quotearg_style (escape_quoting_style, host));
|
quotearg_style (escape_quoting_style, host));
|
||||||
|
|
||||||
return opt.check_cert == CHECK_CERT_ON ? success : true;
|
/* never return true if pinsuccess fails */
|
||||||
|
return !pinsuccess ? false : (opt.check_cert == CHECK_CERT_ON ? success : true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -236,6 +236,11 @@ struct options
|
|||||||
char *ca_cert; /* CA certificate file to use */
|
char *ca_cert; /* CA certificate file to use */
|
||||||
char *crl_file; /* file with CRLs */
|
char *crl_file; /* file with CRLs */
|
||||||
|
|
||||||
|
char *pinnedpubkey; /* Public key (PEM/DER) file, or any number
|
||||||
|
of base64 encoded sha256 hashes preceded by
|
||||||
|
\'sha256//\' and seperated by \';\', to verify
|
||||||
|
peer against */
|
||||||
|
|
||||||
char *random_file; /* file with random data to seed the PRNG */
|
char *random_file; /* file with random data to seed the PRNG */
|
||||||
char *egd_file; /* file name of the egd daemon socket */
|
char *egd_file; /* file name of the egd daemon socket */
|
||||||
bool https_only; /* whether to follow HTTPS only */
|
bool https_only; /* whether to follow HTTPS only */
|
||||||
|
201
src/utils.c
201
src/utils.c
@ -31,6 +31,7 @@ as that of the covered work. */
|
|||||||
|
|
||||||
#include "wget.h"
|
#include "wget.h"
|
||||||
|
|
||||||
|
#include "sha256.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -2521,6 +2522,206 @@ wg_hex_to_string (char *str_buffer, const char *hex_buffer, size_t hex_len)
|
|||||||
str_buffer[2 * i] = '\0';
|
str_buffer[2 * i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public key pem to der conversion
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool
|
||||||
|
wg_pubkey_pem_to_der (const char *pem, unsigned char **der, size_t *der_len)
|
||||||
|
{
|
||||||
|
char *stripped_pem, *begin_pos, *end_pos;
|
||||||
|
size_t pem_count, stripped_pem_count = 0, pem_len;
|
||||||
|
ssize_t size;
|
||||||
|
unsigned char *base64data;
|
||||||
|
|
||||||
|
*der = NULL;
|
||||||
|
*der_len = 0;
|
||||||
|
|
||||||
|
/* if no pem, exit. */
|
||||||
|
if (!pem)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
begin_pos = strstr (pem, "-----BEGIN PUBLIC KEY-----");
|
||||||
|
if (!begin_pos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pem_count = begin_pos - pem;
|
||||||
|
/* Invalid if not at beginning AND not directly following \n */
|
||||||
|
if (0 != pem_count && '\n' != pem[pem_count - 1])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* 26 is length of "-----BEGIN PUBLIC KEY-----" */
|
||||||
|
pem_count += 26;
|
||||||
|
|
||||||
|
/* Invalid if not directly following \n */
|
||||||
|
end_pos = strstr (pem + pem_count, "\n-----END PUBLIC KEY-----");
|
||||||
|
if (!end_pos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pem_len = end_pos - pem;
|
||||||
|
|
||||||
|
stripped_pem = xmalloc (pem_len - pem_count + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we loop through the pem array one character at a time between the
|
||||||
|
* correct indices, and place each character that is not '\n' or '\r'
|
||||||
|
* into the stripped_pem array, which should represent the raw base64 string
|
||||||
|
*/
|
||||||
|
while (pem_count < pem_len) {
|
||||||
|
if ('\n' != pem[pem_count] && '\r' != pem[pem_count])
|
||||||
|
stripped_pem[stripped_pem_count++] = pem[pem_count];
|
||||||
|
++pem_count;
|
||||||
|
}
|
||||||
|
/* Place the null terminator in the correct place */
|
||||||
|
stripped_pem[stripped_pem_count] = '\0';
|
||||||
|
|
||||||
|
base64data = xmalloc (BASE64_LENGTH(stripped_pem_count));
|
||||||
|
|
||||||
|
size = base64_decode (stripped_pem, base64data);
|
||||||
|
|
||||||
|
if (size < 0) {
|
||||||
|
xfree (base64data); /* malformed base64 from server */
|
||||||
|
} else {
|
||||||
|
*der = base64data;
|
||||||
|
*der_len = (size_t) size;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree (stripped_pem);
|
||||||
|
|
||||||
|
return *der_len > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic pinned public key check.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
wg_pin_peer_pubkey (const char *pinnedpubkey, const char *pubkey, size_t pubkeylen)
|
||||||
|
{
|
||||||
|
struct file_memory *fm;
|
||||||
|
unsigned char *buf = NULL, *pem_ptr = NULL;
|
||||||
|
long filesize;
|
||||||
|
size_t size, pem_len;
|
||||||
|
bool pem_read;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
size_t pinkeylen;
|
||||||
|
ssize_t decoded_hash_length;
|
||||||
|
char *pinkeycopy, *begin_pos, *end_pos;
|
||||||
|
unsigned char *sha256sumdigest = NULL, *expectedsha256sumdigest = NULL;
|
||||||
|
|
||||||
|
/* if a path wasn't specified, don't pin */
|
||||||
|
if (!pinnedpubkey)
|
||||||
|
return true;
|
||||||
|
if (!pubkey || !pubkeylen)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* only do this if pinnedpubkey starts with "sha256//", length 8 */
|
||||||
|
if (strncmp (pinnedpubkey, "sha256//", 8) == 0) {
|
||||||
|
/* compute sha256sum of public key */
|
||||||
|
sha256sumdigest = xmalloc (SHA256_DIGEST_SIZE);
|
||||||
|
sha256_buffer (pubkey, pubkeylen, sha256sumdigest);
|
||||||
|
expectedsha256sumdigest = xmalloc (SHA256_DIGEST_SIZE + 1);
|
||||||
|
|
||||||
|
/* it starts with sha256//, copy so we can modify it */
|
||||||
|
pinkeylen = strlen (pinnedpubkey) + 1;
|
||||||
|
pinkeycopy = xmalloc (pinkeylen);
|
||||||
|
memcpy (pinkeycopy, pinnedpubkey, pinkeylen);
|
||||||
|
|
||||||
|
/* point begin_pos to the copy, and start extracting keys */
|
||||||
|
begin_pos = pinkeycopy;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
end_pos = strstr (begin_pos, ";sha256//");
|
||||||
|
/*
|
||||||
|
* if there is an end_pos, null terminate,
|
||||||
|
* otherwise it'll go to the end of the original string
|
||||||
|
*/
|
||||||
|
if (end_pos)
|
||||||
|
end_pos[0] = '\0';
|
||||||
|
|
||||||
|
/* decode base64 pinnedpubkey, 8 is length of "sha256//" */
|
||||||
|
decoded_hash_length = base64_decode (begin_pos + 8, expectedsha256sumdigest);
|
||||||
|
/* if valid base64, compare sha256 digests directly */
|
||||||
|
if (SHA256_DIGEST_SIZE == decoded_hash_length &&
|
||||||
|
!memcmp (sha256sumdigest, expectedsha256sumdigest, SHA256_DIGEST_SIZE)) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* change back the null-terminator we changed earlier,
|
||||||
|
* and look for next begin
|
||||||
|
*/
|
||||||
|
if (end_pos) {
|
||||||
|
end_pos[0] = ';';
|
||||||
|
begin_pos = strstr (end_pos, "sha256//");
|
||||||
|
}
|
||||||
|
} while (end_pos && begin_pos);
|
||||||
|
|
||||||
|
xfree (sha256sumdigest);
|
||||||
|
xfree (expectedsha256sumdigest);
|
||||||
|
xfree (pinkeycopy);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fall back to assuming this is a file path */
|
||||||
|
fm = wget_read_file (pinnedpubkey);
|
||||||
|
if (!fm)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* Check the file's size */
|
||||||
|
if (fm->length < 0 || fm->length > MAX_PINNED_PUBKEY_SIZE)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the size of our certificate is bigger than the file
|
||||||
|
* size then it can't match
|
||||||
|
*/
|
||||||
|
size = (size_t) fm->length;
|
||||||
|
if (pubkeylen > size)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* If the sizes are the same, it can't be base64 encoded, must be der */
|
||||||
|
if (pubkeylen == size) {
|
||||||
|
if (!memcmp (pubkey, fm->content, pubkeylen))
|
||||||
|
result = true;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise we will assume it's PEM and try to decode it
|
||||||
|
* after placing null terminator
|
||||||
|
*/
|
||||||
|
buf = xmalloc (size + 1);
|
||||||
|
memcpy (buf, fm->content, size);
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
pem_read = wg_pubkey_pem_to_der ((const char *) buf, &pem_ptr, &pem_len);
|
||||||
|
/* if it wasn't read successfully, exit */
|
||||||
|
if (!pem_read)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the size of our certificate doesn't match the size of
|
||||||
|
* the decoded file, they can't be the same, otherwise compare
|
||||||
|
*/
|
||||||
|
if (pubkeylen == pem_len && !memcmp (pubkey, pem_ptr, pubkeylen))
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
xfree (buf);
|
||||||
|
xfree (pem_ptr);
|
||||||
|
wget_read_file_free (fm);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_SSL */
|
||||||
|
|
||||||
#ifdef TESTING
|
#ifdef TESTING
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
|
@ -37,6 +37,10 @@ as that of the covered work. */
|
|||||||
/* Constant is using when we don`t know attempted size exactly */
|
/* Constant is using when we don`t know attempted size exactly */
|
||||||
#define UNKNOWN_ATTEMPTED_SIZE -3
|
#define UNKNOWN_ATTEMPTED_SIZE -3
|
||||||
|
|
||||||
|
#ifndef MAX_PINNED_PUBKEY_SIZE
|
||||||
|
#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Macros that interface to malloc, but know about type sizes, and
|
/* Macros that interface to malloc, but know about type sizes, and
|
||||||
cast the result to the appropriate type. The casts are not
|
cast the result to the appropriate type. The casts are not
|
||||||
necessary in standard C, but Wget performs them anyway for the sake
|
necessary in standard C, but Wget performs them anyway for the sake
|
||||||
@ -161,4 +165,9 @@ void wg_hex_to_string (char *str_buffer, const char *hex_buffer, size_t hex_len)
|
|||||||
|
|
||||||
extern unsigned char char_prop[];
|
extern unsigned char char_prop[];
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
/* Check pinned public key. */
|
||||||
|
bool wg_pin_peer_pubkey (const char *pinnedpubkey, const char *pubkey, size_t pubkeylen);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* UTILS_H */
|
#endif /* UTILS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user