326 lines
9.6 KiB
C
326 lines
9.6 KiB
C
/*
|
|
* Copyright 2019-2021 OARC, Inc.
|
|
* Copyright 2017-2018 Akamai Technologies
|
|
* Copyright 2006-2016 Nominum, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "tsig.h"
|
|
|
|
#include "log.h"
|
|
#include "opt.h"
|
|
#include "dns.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <openssl/evp.h>
|
|
#include <time.h>
|
|
|
|
#define TSIG_HMACMD5_NAME "hmac-md5.sig-alg.reg.int"
|
|
#define TSIG_HMACSHA1_NAME "hmac-sha1"
|
|
#define TSIG_HMACSHA224_NAME "hmac-sha224"
|
|
#define TSIG_HMACSHA256_NAME "hmac-sha256"
|
|
#define TSIG_HMACSHA384_NAME "hmac-sha384"
|
|
#define TSIG_HMACSHA512_NAME "hmac-sha512"
|
|
|
|
static unsigned char* decode64(const void* base64, int* len)
|
|
{
|
|
unsigned char* out;
|
|
|
|
assert(base64);
|
|
assert(len);
|
|
assert(*len);
|
|
|
|
out = calloc(1, *len);
|
|
assert(out);
|
|
|
|
int olen = *len;
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_ENCODE_CTX evp;
|
|
EVP_DecodeInit(&evp);
|
|
if (EVP_DecodeUpdate(&evp, out, &olen, base64, *len)) {
|
|
free(out);
|
|
return 0;
|
|
}
|
|
#else
|
|
EVP_ENCODE_CTX* evp = EVP_ENCODE_CTX_new();
|
|
if (!evp) {
|
|
free(out);
|
|
return 0;
|
|
}
|
|
EVP_DecodeInit(evp);
|
|
if (EVP_DecodeUpdate(evp, out, &olen, base64, *len)) {
|
|
free(out);
|
|
EVP_ENCODE_CTX_free(evp);
|
|
return 0;
|
|
}
|
|
EVP_ENCODE_CTX_free(evp);
|
|
#endif
|
|
|
|
*len = olen;
|
|
|
|
return out;
|
|
}
|
|
|
|
perf_tsigkey_t* perf_tsig_parsekey(const char* arg)
|
|
{
|
|
perf_tsigkey_t* tsigkey;
|
|
const char * sep1, *sep2, *alg, *name, *secret;
|
|
size_t alglen, namelen, secretlen;
|
|
int keylen;
|
|
const EVP_MD* md = 0;
|
|
|
|
tsigkey = calloc(1, sizeof(*tsigkey));
|
|
if (!tsigkey) {
|
|
perf_log_fatal("out of memory");
|
|
return 0; // fix clang scan-build
|
|
}
|
|
|
|
sep1 = strchr(arg, ':');
|
|
if (sep1 == NULL) {
|
|
perf_log_warning("invalid TSIG [alg:]name:secret");
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
sep2 = strchr(sep1 + 1, ':');
|
|
if (sep2 == NULL) {
|
|
/* name:key */
|
|
alg = NULL;
|
|
alglen = 0;
|
|
name = arg;
|
|
namelen = sep1 - arg;
|
|
secret = sep1 + 1;
|
|
} else {
|
|
/* [alg:]name:secret */
|
|
alg = arg;
|
|
alglen = sep1 - arg;
|
|
name = sep1 + 1;
|
|
namelen = sep2 - sep1 - 1;
|
|
secret = sep2 + 1;
|
|
}
|
|
|
|
/* Algorithm */
|
|
|
|
if (!alg || !strncasecmp(alg, "hmac-md5:", 9)) {
|
|
md = EVP_md5();
|
|
tsigkey->alg = TSIG_HMACMD5_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACMD5_NAME) - 1;
|
|
} else if (!strncasecmp(alg, "hmac-sha1:", 10)) {
|
|
md = EVP_sha1();
|
|
tsigkey->alg = TSIG_HMACSHA1_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACSHA1_NAME) - 1;
|
|
} else if (!strncasecmp(alg, "hmac-sha224:", 12)) {
|
|
md = EVP_sha224();
|
|
tsigkey->alg = TSIG_HMACSHA224_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACSHA224_NAME) - 1;
|
|
} else if (!strncasecmp(alg, "hmac-sha256:", 12)) {
|
|
md = EVP_sha256();
|
|
tsigkey->alg = TSIG_HMACSHA256_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACSHA256_NAME) - 1;
|
|
} else if (!strncasecmp(alg, "hmac-sha384:", 12)) {
|
|
md = EVP_sha384();
|
|
tsigkey->alg = TSIG_HMACSHA384_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACSHA384_NAME) - 1;
|
|
} else if (!strncasecmp(alg, "hmac-sha512:", 12)) {
|
|
md = EVP_sha512();
|
|
tsigkey->alg = TSIG_HMACSHA512_NAME;
|
|
tsigkey->alglen = sizeof(TSIG_HMACSHA512_NAME) - 1;
|
|
} else {
|
|
perf_log_warning("invalid TSIG algorithm %.*s", (int)alglen, alg);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
if (namelen > sizeof(tsigkey->name)) {
|
|
perf_log_fatal("unable to setup TSIG, name too long");
|
|
// fix clang scan-build / sonarcloud:
|
|
free(tsigkey);
|
|
return 0;
|
|
}
|
|
memcpy(tsigkey->name, name, namelen);
|
|
tsigkey->namelen = namelen;
|
|
for (namelen = 0; namelen < tsigkey->namelen; namelen++) {
|
|
tsigkey->name[namelen] = tolower(tsigkey->name[namelen]);
|
|
}
|
|
|
|
/* Secret */
|
|
|
|
secretlen = strlen(secret);
|
|
if (!secretlen) {
|
|
perf_log_warning("unable to setup TSIG, secret empty");
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
keylen = secretlen;
|
|
unsigned char* key = decode64(secret, &keylen);
|
|
if (!key) {
|
|
perf_log_fatal("unable to setup TSIG, invalid base64 secret");
|
|
}
|
|
|
|
/* Setup HMAC */
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
if (!(tsigkey->hmac = calloc(1, sizeof(*tsigkey->hmac)))) {
|
|
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC context failed to be created");
|
|
}
|
|
HMAC_CTX_init(tsigkey->hmac);
|
|
#else
|
|
if (!(tsigkey->hmac = HMAC_CTX_new())) {
|
|
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC context failed to be created");
|
|
}
|
|
#endif
|
|
|
|
if (!HMAC_Init_ex(tsigkey->hmac, key, keylen, md, 0)) {
|
|
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC init failed");
|
|
}
|
|
|
|
free(key);
|
|
|
|
return tsigkey;
|
|
}
|
|
|
|
void perf_tsig_destroykey(perf_tsigkey_t** tsigkeyp)
|
|
{
|
|
assert(tsigkeyp);
|
|
assert(*tsigkeyp);
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
HMAC_CTX_cleanup((*tsigkeyp)->hmac);
|
|
free((*tsigkeyp)->hmac);
|
|
#else
|
|
HMAC_CTX_free((*tsigkeyp)->hmac);
|
|
#endif
|
|
|
|
free(*tsigkeyp);
|
|
*tsigkeyp = 0;
|
|
}
|
|
|
|
/*
|
|
* Appends a TSIG record to the packet.
|
|
*/
|
|
perf_result_t perf_add_tsig(perf_buffer_t* packet, perf_tsigkey_t* tsigkey)
|
|
{
|
|
unsigned char* base;
|
|
size_t rdlen, totallen;
|
|
unsigned char tmpdata[512], md[EVP_MAX_MD_SIZE];
|
|
unsigned int mdlen;
|
|
perf_buffer_t tmp;
|
|
uint32_t now;
|
|
perf_result_t result;
|
|
|
|
now = time(NULL);
|
|
|
|
if (!HMAC_Init_ex(tsigkey->hmac, 0, 0, 0, 0)) {
|
|
perf_log_fatal("adding TSIG: OpenSSL HMAC reinit failed");
|
|
}
|
|
|
|
if (!HMAC_Update(tsigkey->hmac, perf_buffer_base(packet), perf_buffer_usedlength(packet))) {
|
|
perf_log_fatal("adding TSIG: OpenSSL HMAC update failed");
|
|
}
|
|
|
|
/* Digest the TSIG record */
|
|
perf_buffer_init(&tmp, tmpdata, sizeof tmpdata);
|
|
switch ((result = perf_dname_fromstring(tsigkey->name, tsigkey->namelen, &tmp))) {
|
|
case PERF_R_SUCCESS:
|
|
break;
|
|
case PERF_R_NOSPACE:
|
|
perf_log_warning("adding TSIG: out of space in digest record");
|
|
return result;
|
|
default:
|
|
perf_log_warning("adding TSIG: invalid owner name");
|
|
return result;
|
|
}
|
|
perf_buffer_putuint16(&tmp, 255); /* class ANY */
|
|
perf_buffer_putuint32(&tmp, 0); /* ttl */
|
|
switch ((result = perf_dname_fromstring(tsigkey->alg, tsigkey->alglen, &tmp))) {
|
|
case PERF_R_SUCCESS:
|
|
break;
|
|
case PERF_R_NOSPACE:
|
|
perf_log_warning("adding TSIG: out of space in digest record");
|
|
return result;
|
|
default:
|
|
perf_log_warning("adding TSIG: invalid algorithm name");
|
|
return result;
|
|
}
|
|
perf_buffer_putuint16(&tmp, 0); /* time high */
|
|
perf_buffer_putuint32(&tmp, now); /* time low */
|
|
perf_buffer_putuint16(&tmp, 300); /* fudge */
|
|
perf_buffer_putuint16(&tmp, 0); /* error */
|
|
perf_buffer_putuint16(&tmp, 0); /* other length */
|
|
|
|
if (!HMAC_Update(tsigkey->hmac, perf_buffer_base(&tmp), perf_buffer_usedlength(&tmp))) {
|
|
perf_log_fatal("adding TSIG: OpenSSL HMAC update failed");
|
|
}
|
|
|
|
mdlen = sizeof(md);
|
|
if (!HMAC_Final(tsigkey->hmac, md, &mdlen)) {
|
|
perf_log_fatal("adding TSIG: OpenSSL HMAC final failed");
|
|
}
|
|
|
|
/* Make sure everything will fit */
|
|
rdlen = tsigkey->alglen + 18 + mdlen;
|
|
totallen = tsigkey->namelen + 12 + rdlen;
|
|
if (totallen > perf_buffer_availablelength(packet)) {
|
|
perf_log_warning("adding TSIG: out of space");
|
|
return PERF_R_NOSPACE;
|
|
}
|
|
|
|
base = perf_buffer_base(packet);
|
|
|
|
/* Add the TSIG record. */
|
|
switch ((result = perf_dname_fromstring(tsigkey->name, tsigkey->namelen, packet))) {
|
|
case PERF_R_SUCCESS:
|
|
break;
|
|
case PERF_R_NOSPACE:
|
|
perf_log_warning("adding TSIG: out of space");
|
|
return result;
|
|
default:
|
|
perf_log_warning("adding TSIG: invalid owner name");
|
|
return result;
|
|
}
|
|
perf_buffer_putuint16(packet, 250); /* type TSIG */
|
|
perf_buffer_putuint16(packet, 255); /* class ANY */
|
|
perf_buffer_putuint32(packet, 0); /* ttl */
|
|
perf_buffer_putuint16(packet, rdlen); /* rdlen */
|
|
switch ((result = perf_dname_fromstring(tsigkey->alg, tsigkey->alglen, packet))) {
|
|
case PERF_R_SUCCESS:
|
|
break;
|
|
case PERF_R_NOSPACE:
|
|
perf_log_warning("adding TSIG: out of space");
|
|
return result;
|
|
default:
|
|
perf_log_warning("adding TSIG: invalid algorithm name");
|
|
return result;
|
|
}
|
|
perf_buffer_putuint16(packet, 0); /* time high */
|
|
perf_buffer_putuint32(packet, now); /* time low */
|
|
perf_buffer_putuint16(packet, 300); /* fudge */
|
|
perf_buffer_putuint16(packet, mdlen); /* digest len */
|
|
perf_buffer_putmem(packet, md, mdlen); /* digest */
|
|
perf_buffer_putmem(packet, base, 2); /* orig ID */
|
|
perf_buffer_putuint16(packet, 0); /* error */
|
|
perf_buffer_putuint16(packet, 0); /* other len */
|
|
|
|
base[11]++; /* increment additional record count */
|
|
|
|
return PERF_R_SUCCESS;
|
|
}
|