Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,7 @@ AC_CHECK_FUNCS([ \
BIO_sock_non_fatal_error \
CRYPTO_set_mem_functions \
HMAC_CTX_new \
EVP_MAC_CTX_new \
X509_get0_signature \
ERR_get_error_all \
])
Expand Down
133 changes: 94 additions & 39 deletions plugins/s3_auth/aws_auth_v4.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
* @see aws_auth_v4.h
*/

#include <cstring> /* strlen() */
#include <string> /* stoi() */
#include <ctime> /* strftime(), time(), gmtime_r() */
#include <iomanip> /* std::setw */
#include <sstream> /* std::stringstream */
#include <openssl/sha.h> /* SHA(), sha256_Update(), SHA256_Final, etc. */
#include <cstring> /* strlen() */
#include <string> /* stoi() */
#include <ctime> /* strftime(), time(), gmtime_r() */
#include <iomanip> /* std::setw */
#include <sstream> /* std::stringstream */

#include "tscore/ink_config.h"

#include <openssl/sha.h> /* SHA256_DIGEST_LENGTH */
#include <openssl/evp.h> /* EVP_DigestInit_ex, etc.*/
#include <openssl/hmac.h> /* HMAC() */
#if defined(HAVE_EVP_MAC_CTX_NEW)
#include <openssl/core_names.h> /* OSSL_MAC_PARAM_KEY, etc. */
#endif

#ifdef AWS_AUTH_V4_DETAILED_DEBUG_OUTPUT
#include <iostream>
Expand Down Expand Up @@ -217,30 +224,71 @@ trimWhiteSpaces(const String &s)
* Group of static inline helper function for less error prone parameter handling and unit test logging.
*/
inline static void
sha256Update(SHA256_CTX *ctx, const char *in, size_t inLen)
sha256Update(EVP_MD_CTX *ctx, const char *in, size_t inLen)
{
SHA256_Update(ctx, in, inLen);
EVP_DigestUpdate(ctx, in, inLen);
#ifdef AWS_AUTH_V4_DETAILED_DEBUG_OUTPUT
std::cout << String(in, inLen);
#endif
}

inline static void
sha256Update(SHA256_CTX *ctx, const char *in)
sha256Update(EVP_MD_CTX *ctx, const char *in)
{
sha256Update(ctx, in, strlen(in));
}

inline static void
sha256Update(SHA256_CTX *ctx, const String &in)
sha256Update(EVP_MD_CTX *ctx, const String &in)
{
sha256Update(ctx, in.c_str(), in.length());
}

inline static void
sha256Final(unsigned char hex[SHA256_DIGEST_LENGTH], SHA256_CTX *ctx)
sha256Final(unsigned char hex[SHA256_DIGEST_LENGTH], EVP_MD_CTX *ctx)
{
EVP_DigestFinal_ex(ctx, hex, nullptr);
}

static unsigned char *
hmac_sha256(const void *key, int key_len, const unsigned char *data, int data_len, unsigned char *md, unsigned int *md_len)
{
SHA256_Final(hex, ctx);
#if defined(HAVE_EVP_MAC_CTX_NEW)
unsigned char *result = nullptr;

if (EVP_MAC *mac = EVP_MAC_fetch(nullptr, "HMAC", nullptr); mac) {
if (EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac); ctx) {
const OSSL_PARAM params[] = {
OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, const_cast<void *>(key), key_len),
OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, const_cast<char *>(SN_sha256), 0),
OSSL_PARAM_construct_end(),
};
bool ret = EVP_MAC_CTX_set_params(ctx, params);

if (ret) {
ret = EVP_MAC_init(ctx);
}
if (ret) {
ret = EVP_MAC_update(ctx, data, data_len);
}
if (ret) {
size_t len;
ret = EVP_MAC_final(ctx, md, &len, SHA256_DIGEST_LENGTH); // Assuming md has enough space as HMAC() does
*md_len = len;
}
if (ret) {
result = md;
}

EVP_MAC_CTX_free(ctx);
}
EVP_MAC_free(mac);
}

return result;
#else
return HMAC(EVP_sha256(), key, key_len, data, data_len, md, md_len);
#endif
}

/**
Expand All @@ -259,8 +307,13 @@ getPayloadSha256(bool signPayload)
return UNSIGNED_PAYLOAD;
}

unsigned char payloadHash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char *>(""), 0, payloadHash); /* empty content */
unsigned char payloadHash[SHA256_DIGEST_LENGTH] = {0};
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr)) {
EVP_DigestUpdate(ctx, reinterpret_cast<const unsigned char *>(""), 0); /* empty content */
EVP_DigestFinal_ex(ctx, payloadHash, nullptr);
}
EVP_MD_CTX_free(ctx);

return base16Encode(reinterpret_cast<char *>(payloadHash), SHA256_DIGEST_LENGTH);
}
Expand All @@ -285,27 +338,28 @@ getCanonicalRequestSha256Hash(TsInterface &api, bool signPayload, const StringSe
int length;
const char *str = nullptr;
unsigned char canonicalRequestSha256Hash[SHA256_DIGEST_LENGTH];
SHA256_CTX canonicalRequestSha256Ctx;
EVP_MD_CTX *canonicalRequestSha256Ctx;

SHA256_Init(&canonicalRequestSha256Ctx);
canonicalRequestSha256Ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(canonicalRequestSha256Ctx, EVP_sha256(), nullptr);

#ifdef AWS_AUTH_V4_DETAILED_DEBUG_OUTPUT
std::cout << "<CanonicalRequest>";
#endif

/* <HTTPMethod>\n */
str = api.getMethod(&length);
sha256Update(&canonicalRequestSha256Ctx, str, length);
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, str, length);
sha256Update(canonicalRequestSha256Ctx, "\n");

/* URI Encoded Canonical URI
* <CanonicalURI>\n */
str = api.getPath(&length);
String path("/");
path.append(str, length);
String canonicalUri = canonicalEncode(path, /* isObjectName */ true);
sha256Update(&canonicalRequestSha256Ctx, canonicalUri);
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, canonicalUri);
sha256Update(canonicalRequestSha256Ctx, "\n");

/* Sorted Canonical Query String
* <CanonicalQueryString>\n */
Expand Down Expand Up @@ -335,8 +389,8 @@ getCanonicalRequestSha256Hash(TsInterface &api, bool signPayload, const StringSe
queryStr.append(paramName);
queryStr.append("=").append(paramsMap[paramName]);
}
sha256Update(&canonicalRequestSha256Ctx, queryStr);
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, queryStr);
sha256Update(canonicalRequestSha256Ctx, "\n");

/* Sorted Canonical Headers
* <CanonicalHeaders>\n */
Expand Down Expand Up @@ -393,12 +447,12 @@ getCanonicalRequestSha256Hash(TsInterface &api, bool signPayload, const StringSe
}

for (const auto &it : signedHeadersSet) {
sha256Update(&canonicalRequestSha256Ctx, it);
sha256Update(&canonicalRequestSha256Ctx, ":");
sha256Update(&canonicalRequestSha256Ctx, headersMap[it]);
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, it);
sha256Update(canonicalRequestSha256Ctx, ":");
sha256Update(canonicalRequestSha256Ctx, headersMap[it]);
sha256Update(canonicalRequestSha256Ctx, "\n");
}
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, "\n");

for (const auto &it : signedHeadersSet) {
if (!signedHeaders.empty()) {
Expand All @@ -407,19 +461,20 @@ getCanonicalRequestSha256Hash(TsInterface &api, bool signPayload, const StringSe
signedHeaders.append(it);
}

sha256Update(&canonicalRequestSha256Ctx, signedHeaders);
sha256Update(&canonicalRequestSha256Ctx, "\n");
sha256Update(canonicalRequestSha256Ctx, signedHeaders);
sha256Update(canonicalRequestSha256Ctx, "\n");

/* Hex(SHA256Hash(<payload>) (no new-line char at end)
* @TODO support non-empty content, i.e. POST */
String payloadSha256Hash = getPayloadSha256(signPayload);
sha256Update(&canonicalRequestSha256Ctx, payloadSha256Hash);
sha256Update(canonicalRequestSha256Ctx, payloadSha256Hash);

/* Hex(SHA256Hash(<CanonicalRequest>)) */
sha256Final(canonicalRequestSha256Hash, &canonicalRequestSha256Ctx);
sha256Final(canonicalRequestSha256Hash, canonicalRequestSha256Ctx);
#ifdef AWS_AUTH_V4_DETAILED_DEBUG_OUTPUT
std::cout << "</CanonicalRequest>" << std::endl;
#endif
EVP_MD_CTX_free(canonicalRequestSha256Ctx);
return base16Encode(reinterpret_cast<char *>(canonicalRequestSha256Hash), SHA256_DIGEST_LENGTH);
}

Expand Down Expand Up @@ -663,14 +718,14 @@ getSignature(const char *awsSecret, size_t awsSecretLen, const char *awsRegion,
memcpy(key + 4, awsSecret, awsSecretLen);

unsigned int len = signatureLen;
if (HMAC(EVP_sha256(), key, keyLen, (unsigned char *)dateTime, dateTimeLen, dateKey, &dateKeyLen) &&
HMAC(EVP_sha256(), dateKey, dateKeyLen, (unsigned char *)awsRegion, awsRegionLen, dateRegionKey, &dateRegionKeyLen) &&
HMAC(EVP_sha256(), dateRegionKey, dateRegionKeyLen, (unsigned char *)awsService, awsServiceLen, dateRegionServiceKey,
&dateRegionServiceKeyLen) &&
HMAC(EVP_sha256(), dateRegionServiceKey, dateRegionServiceKeyLen, reinterpret_cast<const unsigned char *>("aws4_request"), 12,
signingKey, &signingKeyLen) &&
HMAC(EVP_sha256(), signingKey, signingKeyLen, (unsigned char *)stringToSign, stringToSignLen,
reinterpret_cast<unsigned char *>(signature), &len)) {
if (hmac_sha256(key, keyLen, (unsigned char *)dateTime, dateTimeLen, dateKey, &dateKeyLen) &&
hmac_sha256(dateKey, dateKeyLen, (unsigned char *)awsRegion, awsRegionLen, dateRegionKey, &dateRegionKeyLen) &&
hmac_sha256(dateRegionKey, dateRegionKeyLen, (unsigned char *)awsService, awsServiceLen, dateRegionServiceKey,
&dateRegionServiceKeyLen) &&
hmac_sha256(dateRegionServiceKey, dateRegionServiceKeyLen, reinterpret_cast<const unsigned char *>("aws4_request"), 12,
signingKey, &signingKeyLen) &&
hmac_sha256(signingKey, signingKeyLen, (unsigned char *)stringToSign, stringToSignLen,
reinterpret_cast<unsigned char *>(signature), &len)) {
return len;
}

Expand Down
86 changes: 69 additions & 17 deletions plugins/s3_auth/s3_auth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@
#include <string>
#include <unordered_map>

#include "tscore/ink_config.h"

#include <openssl/sha.h>
#include <openssl/hmac.h>
#if defined(HAVE_EVP_MAC_CTX_NEW)
#include <openssl/core_names.h>
#endif

#include <ts/ts.h>
#include <ts/remap.h>
#include "tscore/ink_config.h"

#include "aws_auth_v4.h"

Expand Down Expand Up @@ -131,6 +135,23 @@ loadRegionMap(StringMap &m, const String &filename)
return true;
}

/**
* A wrapper function to absorb API difference among OpenSSL versions
*/
#if defined(HAVE_EVP_MAC_CTX_NEW)
inline static int
s3_auth_HMAC_Update(EVP_MAC_CTX *ctx, const unsigned char *data, size_t datalen)
{
return EVP_MAC_update(ctx, data, datalen);
}
#else
inline static int
s3_auth_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t datalen)
{
return HMAC_Update(ctx, data, datalen);
}
#endif

///////////////////////////////////////////////////////////////////////////////
// Cache for the secrets file, to avoid reading / loading them repeatedly on
// a reload of remap.config. This gets cached for 60s (not configurable).
Expand Down Expand Up @@ -799,44 +820,75 @@ S3Request::authorizeV2(S3Config *s3)
}

// Produce the SHA1 MAC digest
#ifndef HAVE_HMAC_CTX_NEW
#if defined(HAVE_EVP_MAC_CTX_NEW)
EVP_MAC *mac;
EVP_MAC_CTX *ctx;
#elif !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX ctx[1];
#else
HMAC_CTX *ctx;
#endif

#if defined(HAVE_EVP_MAC_CTX_NEW)
size_t hmac_len;
#else
unsigned int hmac_len;
#endif
size_t hmac_b64_len;
unsigned char hmac[SHA_DIGEST_LENGTH];
char hmac_b64[SHA_DIGEST_LENGTH * 2];

#ifndef HAVE_HMAC_CTX_NEW
#if defined(HAVE_EVP_MAC_CTX_NEW)
mac = EVP_MAC_fetch(nullptr, "HMAC", nullptr);
ctx = EVP_MAC_CTX_new(mac);
#elif !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX_init(ctx);
#else
ctx = HMAC_CTX_new();
#endif

#if defined(HAVE_EVP_MAC_CTX_NEW)
const OSSL_PARAM params[] = {
OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, const_cast<char *>(s3->secret()), s3->secret_len()),
OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, const_cast<char *>(SN_sha1), 0),
OSSL_PARAM_construct_end(),
};
EVP_MAC_CTX_set_params(ctx, params);
EVP_MAC_init(ctx);
#else
HMAC_Init_ex(ctx, s3->secret(), s3->secret_len(), EVP_sha1(), nullptr);
HMAC_Update(ctx, (unsigned char *)method, method_len);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
HMAC_Update(ctx, (unsigned char *)con_md5, con_md5_len);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
HMAC_Update(ctx, (unsigned char *)con_type, con_type_len);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
HMAC_Update(ctx, reinterpret_cast<unsigned char *>(date), date_len);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n/"), 2);
#endif

s3_auth_HMAC_Update(ctx, (unsigned char *)method, method_len);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
s3_auth_HMAC_Update(ctx, (unsigned char *)con_md5, con_md5_len);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
s3_auth_HMAC_Update(ctx, (unsigned char *)con_type, con_type_len);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n"), 1);
s3_auth_HMAC_Update(ctx, reinterpret_cast<unsigned char *>(date), date_len);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("\n/"), 2);

if (host && host_endp) {
HMAC_Update(ctx, (unsigned char *)host, host_endp - host);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("/"), 1);
s3_auth_HMAC_Update(ctx, (unsigned char *)host, host_endp - host);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>("/"), 1);
}

HMAC_Update(ctx, (unsigned char *)path, path_len);
s3_auth_HMAC_Update(ctx, (unsigned char *)path, path_len);
if (param) {
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>(";"), 1); // TSUrlHttpParamsGet() does not include ';'
HMAC_Update(ctx, (unsigned char *)param, param_len);
s3_auth_HMAC_Update(ctx, reinterpret_cast<const unsigned char *>(";"), 1); // TSUrlHttpParamsGet() does not include ';'
s3_auth_HMAC_Update(ctx, (unsigned char *)param, param_len);
}

#if defined(HAVE_EVP_MAC_CTX_NEW)
EVP_MAC_final(ctx, hmac, &hmac_len, sizeof(hmac));
#else
HMAC_Final(ctx, hmac, &hmac_len);
#ifndef HAVE_HMAC_CTX_NEW
#endif

#if defined(HAVE_EVP_MAC_CTX_NEW)
EVP_MAC_CTX_free(ctx);
EVP_MAC_free(mac);
#elif !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX_cleanup(ctx);
#else
HMAC_CTX_free(ctx);
Expand Down