From aadbbd7d080ef1662bfd7ea568edbb29f25fdd72 Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Tue, 30 Dec 2014 01:09:27 -0800 Subject: [PATCH] Crypto-NG: Base64 crypto replacement The existing Squid base64 code had ambiguous copyright licensing. In particular it only referenced a dead URL for source copyright ownership details. In all likelihood this was for an Open Source implementation, but we dont have sufficient record of the original license terms to be certain without a long investigation. It has also been heavily modified and customized over the decades since importing whih complicates the issue a lot. It also does not match any of the common industry context-based API patterns for encoders/decoders. This patch replaces that logic with GPLv2 licensed code from the Nettle crypto library. Either linking the library dynamically or in its absence embedding the logic via our libmiscencoding library. It also updates all code to the new API, and as a byproduct removes several layers of deprecated wrapper functions which have grown in over the years. --- CREDITS | 35 ++ configure.ac | 2 +- .../kerberos/negotiate_kerberos_auth.cc | 25 +- .../kerberos/negotiate_kerberos_auth_test.cc | 37 +- .../kerberos/negotiate_kerberos_pac.cc | 19 +- .../wrapper/negotiate_wrapper.cc | 21 +- helpers/ntlm_auth/fake/ntlm_fake_auth.cc | 26 +- helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.cc | 33 +- include/base64.h | 94 +++-- lib/base64.c | 365 ++++++++++-------- src/HttpHeader.cc | 8 +- src/adaptation/icap/ModXact.cc | 36 +- src/auth/digest/Config.cc | 6 +- src/http.cc | 42 +- src/peer_proxy_negotiate_auth.cc | 12 +- tools/cachemgr.cc | 29 +- tools/squidclient/gssapi_support.cc | 24 +- tools/squidclient/squidclient.cc | 25 +- 18 files changed, 559 insertions(+), 280 deletions(-) diff --git a/CREDITS b/CREDITS index c7ae1188a1..4099183a79 100644 --- a/CREDITS +++ b/CREDITS @@ -1482,6 +1482,41 @@ lib/snmplib/snmp_vars.c: ============================================================================== +include/base64.h: +lib/base64.c: + +/* + Copyright (C) 2002 Niels Möller, Dan Egnor + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +============================================================================== + include/heap.h, lib/heap.cc: diff --git a/configure.ac b/configure.ac index bcf67bbf68..01ea31261a 100644 --- a/configure.ac +++ b/configure.ac @@ -1199,7 +1199,7 @@ case "$with_nettle" in if test "x$with_nettle" != "xno" ; then AC_CHECK_LIB(nettle, nettle_md5_init,[ NETTLELIB="$NETTLELIBDIR -lnettle" - AC_CHECK_HEADERS(nettle/md5.h) + AC_CHECK_HEADERS(nettle/md5.h nettle/base64.h) ],[with_nettle=no]) fi AC_MSG_NOTICE([Using Nettle cryptographic library: ${with_nettle:=yes}]) diff --git a/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc b/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc index bf209decf6..ef2c780b22 100644 --- a/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc +++ b/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc @@ -656,12 +656,22 @@ main(int argc, char *const argv[]) fprintf(stdout, "BH Invalid negotiate request\n"); continue; } - input_token.length = (size_t)base64_decode_len(buf+3); + const uint8_t *b64Token = reinterpret_cast(buf+3); + input_token.length = BASE64_DECODE_LENGTH(strlen(buf+3)); debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n", - LogTime(), PROGRAM, buf + 3, (int) input_token.length); + LogTime(), PROGRAM, b64Token, (int) input_token.length); input_token.value = xmalloc(input_token.length); - input_token.length = (size_t)base64_decode((char *) input_token.value, (unsigned int)input_token.length, buf+3); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t dstLen = 0; + if (!base64_decode_update(&ctx, &dstLen, static_cast(input_token.value), input_token.length, b64Token) || + !base64_decode_final(&ctx)) { + debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM, b64Token); + fprintf(stdout, "BH Invalid negotiate request token\n"); + continue; + } + input_token.length = dstLen; if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { @@ -708,14 +718,17 @@ main(int argc, char *const argv[]) if (output_token.length) { spnegoToken = (const unsigned char *) output_token.value; spnegoTokenLength = output_token.length; - token = (char *) xmalloc((size_t)base64_encode_len((int)spnegoTokenLength)); + token = (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength)); if (token == NULL) { debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } - base64_encode_str(token, base64_encode_len((int)spnegoTokenLength), - (const char *) spnegoToken, (int)spnegoTokenLength); + struct base64_encode_ctx tokCtx; + base64_encode_init(&tokCtx); + size_t blen = base64_encode_update(&tokCtx, reinterpret_cast(token), spnegoTokenLength, reinterpret_cast(spnegoToken)); + blen += base64_encode_final(&tokCtx, reinterpret_cast(token)+blen); + token[blen] = '\0'; if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1)) goto cleanup; diff --git a/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth_test.cc b/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth_test.cc index c5031ceb2a..d45f8ad189 100644 --- a/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth_test.cc +++ b/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth_test.cc @@ -186,26 +186,25 @@ squid_kerb_proxy_auth(char *proxy) major_status = gss_import_name(&minor_status, &service, gss_nt_service_name, &server_name); - if (check_gss_err(major_status, minor_status, "gss_import_name()")) - goto cleanup; - - major_status = gss_init_sec_context(&minor_status, - GSS_C_NO_CREDENTIAL, &gss_context, server_name, - gss_mech_spnego, - 0, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, NULL, &output_token, NULL, NULL); - - if (check_gss_err(major_status, minor_status, "gss_init_sec_context()")) - goto cleanup; - - if (output_token.length) { - token = (char *) xmalloc((size_t)base64_encode_len((int)output_token.length)); - base64_encode_str(token, base64_encode_len((int)output_token.length), - (const char *) output_token.value, (int)output_token.length); + if (!check_gss_err(major_status, minor_status, "gss_import_name()")) { + + major_status = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, &gss_context, server_name, + gss_mech_spnego, + 0, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, NULL, &output_token, NULL, NULL); + + if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()") && output_token.length) { + token = (char *) xcalloc(base64_encode_len(output_token.length), 1); + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, reinterpret_cast(token), output_token.length, reinterpret_cast(output_token.value)); + blen += base64_encode_final(&ctx, reinterpret_cast(token)+blen); + } } -cleanup: + gss_delete_sec_context(&minor_status, &gss_context, NULL); gss_release_buffer(&minor_status, &service); gss_release_buffer(&minor_status, &input_token); diff --git a/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc b/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc index d5078b78eb..6f04f99844 100644 --- a/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc +++ b/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc @@ -230,10 +230,17 @@ getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t Gro LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); } } - if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)(length+4)))) { + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + uint8_t *b64buf = (uint8_t *)xcalloc(base64_encode_len(length+4)*sizeof(uint8_t),1); + size_t blen = base64_encode_update(&ctx, b64buf, length+4, reinterpret_cast(ag)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[sizeof(*b64buf)-1] = '\0'; + if (!pstrcat(ad_groups, reinterpret_cast(b64buf))) { debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); } + xfree(b64buf); xfree(ag); } @@ -302,10 +309,18 @@ getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount) LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); } } - if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)length))) { + + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + uint8_t *b64buf = (uint8_t *)xcalloc(base64_encode_len(length)*sizeof(uint8_t),1); + size_t blen = base64_encode_update(&ctx, b64buf, length, reinterpret_cast(ag)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[sizeof(*b64buf)-1] = '\0'; + if (!pstrcat(ad_groups, reinterpret_cast(b64buf))) { debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); } + xfree(b64buf); xfree(ag); rev = get1byt(); diff --git a/helpers/negotiate_auth/wrapper/negotiate_wrapper.cc b/helpers/negotiate_auth/wrapper/negotiate_wrapper.cc index ef6a5487a4..82f4c82688 100644 --- a/helpers/negotiate_auth/wrapper/negotiate_wrapper.cc +++ b/helpers/negotiate_auth/wrapper/negotiate_wrapper.cc @@ -103,7 +103,7 @@ main(int argc, char *const argv[]) int length; int nstart = 0, kstart = 0; int nend = 0, kend = 0; - char *token; + uint8_t *token; char **nargs, **kargs; int fpid; FILE *FDKIN,*FDKOUT; @@ -329,17 +329,28 @@ main(int argc, char *const argv[]) fprintf(stdout, "BH Invalid negotiate request\n"); continue; } - length = base64_decode_len(buf+3); + length = BASE64_DECODE_LENGTH(strlen(buf+3)); if (debug) fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %d).\n", LogTime(), PROGRAM, buf + 3, (int) length); - if ((token = (char *)xmalloc(length)) == NULL) { + if ((token = static_cast(xmalloc(length))) == NULL) { fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM); return 1; } - length = base64_decode(token, length, buf+3); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t dstLen = 0; + if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), reinterpret_cast(buf+3)) || + !base64_decode_final(&ctx)) { + if (debug) + fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3); + fprintf(stdout, "BH Invalid negotiate request token\n"); + continue; + } + length = dstLen; + token[dstLen] = '\0'; if ((static_cast(length) >= sizeof(ntlmProtocol) + 1) && (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) { @@ -375,7 +386,7 @@ main(int argc, char *const argv[]) strcpy(buff,tbuff); } } else { - free(token); + xfree(token); if (debug) fprintf(stderr, "%s| %s: received Kerberos token\n", LogTime(), PROGRAM); diff --git a/helpers/ntlm_auth/fake/ntlm_fake_auth.cc b/helpers/ntlm_auth/fake/ntlm_fake_auth.cc index ecdc5da324..1e74f2898e 100644 --- a/helpers/ntlm_auth/fake/ntlm_fake_auth.cc +++ b/helpers/ntlm_auth/fake/ntlm_fake_auth.cc @@ -131,14 +131,13 @@ main(int argc, char *argv[]) { char buf[HELPER_INPUT_BUFFER]; int buflen = 0; - char decodedBuf[HELPER_INPUT_BUFFER]; + uint8_t decodedBuf[HELPER_INPUT_BUFFER]; int decodedLen; char user[NTLM_MAX_FIELD_LENGTH], domain[NTLM_MAX_FIELD_LENGTH]; char *p; ntlmhdr *packet = NULL; char helper_command[3]; int len; - char *data = NULL; setbuf(stdout, NULL); setbuf(stderr, NULL); @@ -156,13 +155,19 @@ main(int argc, char *argv[]) if ((p = strchr(buf, '\n')) != NULL) *p = '\0'; /* strip \n */ buflen = strlen(buf); /* keep this so we only scan the buffer for \0 once per loop */ - if (buflen > 3) { - decodedLen = base64_decode(decodedBuf, sizeof(decodedBuf), buf+3); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t dstLen = 0; + if (buflen > 3 && + base64_decode_update(&ctx, &dstLen, decodedBuf, buflen-3, reinterpret_cast(buf+3)) && + base64_decode_final(&ctx)) { + decodedLen = dstLen; packet = (ntlmhdr*)decodedBuf; } else { packet = NULL; decodedLen = 0; } + if (buflen > 3 && NTLM_packet_debug_enabled) { strncpy(helper_command, buf, 2); helper_command[2] = '\0'; @@ -185,13 +190,20 @@ main(int argc, char *argv[]) chal.context_high = htole32(0x003a<<16); len = sizeof(chal) - sizeof(chal.payload) + le16toh(chal.target.maxlen); - data = (char *) base64_encode_bin((char *) &chal, len); + + struct base64_encode_ctx eCtx; + base64_encode_init(&eCtx); + uint8_t *data = (uint8_t*)xcalloc(base64_encode_len(len), 1); + size_t blen = base64_encode_update(&eCtx, data, len, reinterpret_cast(&chal)); + blen += base64_encode_final(&eCtx, data+blen); if (NTLM_packet_debug_enabled) { - printf("TT %s\n", data); + printf("TT %.*s\n", blen, data); debug("sending 'TT' to squid with data:\n"); hex_dump((unsigned char *)&chal, len); } else - SEND2("TT %s", data); + SEND2("TT %.*s", blen, data); + safe_free(data); + } else if (strncmp(buf, "KK ", 3) == 0) { if (!packet) { SEND("BH received KK with no data! user="); diff --git a/helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.cc b/helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.cc index 48a76c9ac4..d084a04545 100644 --- a/helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.cc +++ b/helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.cc @@ -24,6 +24,7 @@ #include "squid.h" #include "base64.h" #include "compat/debug.h" +#include "helpers/defines.h" #include "ntlmauth/ntlmauth.h" #include "ntlmauth/support_bits.cci" #include "rfcnb/rfcnb.h" @@ -181,6 +182,7 @@ make_challenge(char *domain, char *domain_controller) if (init_challenge(my_domain, my_domain_controller) > 0) { return NULL; } + ntlm_challenge chal; uint32_t flags = NTLM_REQUEST_NON_NT_SESSION_KEY | NTLM_CHALLENGE_TARGET_IS_DOMAIN | @@ -189,8 +191,22 @@ make_challenge(char *domain, char *domain_controller) NTLM_NEGOTIATE_USE_LM | NTLM_NEGOTIATE_ASCII; ntlm_make_challenge(&chal, my_domain, my_domain_controller, (char *)challenge, NTLM_NONCE_LEN, flags); - int len = sizeof(chal) - sizeof(chal.payload) + le16toh(chal.target.maxlen); - return base64_encode_bin((char *)&chal, len); + + size_t len = sizeof(chal) - sizeof(chal.payload) + le16toh(chal.target.maxlen); + // for lack of a good NTLM token size limit, allow up to what the helper input can be + // validations later will expect to be limited to that size. + static uint8_t b64buf[HELPER_INPUT_BUFFER-10]; /* 10 for other line fields, delimiters and terminator */ + if (base64_encode_len(len) < sizeof(b64buf)-1) { + debug("base64 encoding of the token challenge will exceed %d bytes", sizeof(b64buf)); + return NULL; + } + + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, b64buf, len, reinterpret_cast(&chal)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[blen] = '\0'; + return reinterpret_cast(b64buf); } /* returns NULL on failure, or a pointer to @@ -479,10 +495,19 @@ manage_request() if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */ /* figure out what we got */ - int decodedLen = base64_decode(decoded, sizeof(decoded), buf+3); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t dstLen = 0; + int decodedLen = 0; + if (!base64_decode_update(&ctx, &dstLen, reinterpret_cast(decoded), strlen(buf)-3, reinterpret_cast(buf+3)) || + !base64_decode_final(&ctx)) { + SEND("NA Packet format error, couldn't base64-decode"); + return; + } + decodedLen = dstLen; if ((size_t)decodedLen < sizeof(ntlmhdr)) { /* decoding failure, return error */ - SEND("NA Packet format error, couldn't base64-decode"); + SEND("NA Packet format error, truncated packet header."); return; } /* fast-track-decode request type. */ diff --git a/include/base64.h b/include/base64.h index 87b3e70357..35cc7ef710 100644 --- a/include/base64.h +++ b/include/base64.h @@ -9,52 +9,88 @@ #ifndef _SQUID_BASE64_H #define _SQUID_BASE64_H +#if HAVE_NETTLE_BASE64_H +#include + +#else /* Base64 functions copied from Nettle 3.0 under GPLv2, with adjustments */ + #ifdef __cplusplus extern "C" { #endif // Decoding functions -/// Calculate the decoded length of a given nul-terminated encoded string. -/// NULL pointer and empty strings are accepted, result is zero. -/// Any return value <= zero means no decoded result can be produced. -extern int base64_decode_len(const char *encodedData); +/// Maximum length of output for base64_decode_update. +/// We have at most 6 buffered bits, and a total of (length + 1) * 6 bits. +# define BASE64_DECODE_LENGTH(length) ((((length) + 1) * 6) / 8) + +struct base64_decode_ctx +{ + unsigned word; /* Leftover bits */ + unsigned bits; /* Number buffered bits */ + + /* Number of padding characters encountered */ + unsigned padding; +}; + +void base64_decode_init(struct base64_decode_ctx *ctx); -/// Decode a base-64 encoded blob into a provided buffer. -/// Will not terminate the resulting string. -/// In-place decoding overlap is supported if result is equal or earlier that the source pointer. -/// -/// \return number of bytes filled in result. -extern int base64_decode(char *result, unsigned int result_max_size, const char *encoded); +/* Returns 1 on success, 0 on error. DST should point to an area of + * size at least BASE64_DECODE_LENGTH(length). The amount of data + * generated is returned in *DST_LENGTH. + */ +int base64_decode_update(struct base64_decode_ctx *ctx, + size_t *dst_length, + uint8_t *dst, + size_t src_length, + const uint8_t *src); + +/* Returns 1 on success. */ +int base64_decode_final(struct base64_decode_ctx *ctx); // Encoding functions -/// Calculate the buffer size required to hold the encoded form of -/// a string of length 'decodedLen' including all terminator bytes. -extern int base64_encode_len(int decodedLen); +/* Maximum length of output for base64_encode_update. NOTE: Doesn't + * include any padding that base64_encode_final may add. */ +/* We have at most 4 buffered bits, and a total of (4 + length * 8) bits. */ +# define BASE64_ENCODE_LENGTH(length) (((length) * 8 + 4)/6) -/// Base-64 encode a string into a given buffer. -/// Will not terminate the resulting string. -/// \return the number of bytes filled in result. -extern int base64_encode(char *result, int result_max_size, const char *data, int data_size); +/* Maximum length of output generated by base64_encode_final. */ +# define BASE64_ENCODE_FINAL_LENGTH 3 -/// Base-64 encode a string into a given buffer. -/// Will terminate the resulting string. -/// \return the number of bytes filled in result. Including the terminator. -extern int base64_encode_str(char *result, int result_max_size, const char *data, int data_size); +/* Exact length of output generated by base64_encode_raw, including + * padding. + */ +# define BASE64_ENCODE_RAW_LENGTH(length) ((((length) + 2)/3)*4) -// Old encoder. Now a wrapper for the new. Takes a binary array of known length. -// Output is presented in a static buffer which will only remain valid until next call. -// Ensures a nul-terminated result. Will always return non-NULL. -extern const char *base64_encode_bin(const char *data, int len); +struct base64_encode_ctx +{ + unsigned word; /* Leftover bits */ + unsigned bits; /* Number of bits, always 0, 2, or 4. */ +}; -// Old encoder. Now a wrapper for the new. -// Output is presented in a static buffer which will only remain valid until next call. -// Ensures a nul-terminated result. Will always return non-NULL. -extern const char *old_base64_encode(const char *decoded); +void base64_encode_init(struct base64_encode_ctx *ctx); + +/// Encodes a single byte. Returns amount of output (always 1 or 2). +size_t base64_encode_single(struct base64_encode_ctx *ctx, uint8_t *dst, uint8_t src); + +/* Returns the number of output characters. DST should point to an + * area of size at least BASE64_ENCODE_LENGTH(length). + */ +size_t base64_encode_update(struct base64_encode_ctx *ctx, uint8_t *dst, size_t length, const uint8_t *src); + +/// DST should point to an area of size at least BASE64_ENCODE_FINAL_LENGTH +size_t base64_encode_final(struct base64_encode_ctx *ctx, uint8_t *dst); #ifdef __cplusplus } #endif + +#endif /* HAVE_NETTLE_BASE64_H */ + +/// Calculate the buffer size required to hold the encoded form of +/// a string of length 'decodedLen' including all terminator bytes. +# define base64_encode_len(length) (BASE64_ENCODE_LENGTH(length)+BASE64_ENCODE_FINAL_LENGTH+1) + #endif /* _SQUID_BASE64_H */ diff --git a/lib/base64.c b/lib/base64.c index a9d19e31f6..3926045fe2 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -7,209 +7,266 @@ */ /* - * Encoders adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments. +* Copied from Nettle 3.0 under GPLv2, with adjustments */ #include "squid.h" #include "base64.h" +#if !HAVE_NETTLE_BASE64_H + #if HAVE_STDLIB_H #include #endif -static void base64_init(void); +static const uint8_t encode_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; -static int base64_initialized = 0; -#define BASE64_VALUE_SZ 256 -#define BASE64_RESULT_SZ 8192 -int base64_value[BASE64_VALUE_SZ]; -const char base64_code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define ENCODE(x) (encode_table[0x3F & (x)]) -static void -base64_init(void) +static const signed char decode_table[0x100] = { - int i; + /* White space is HT, VT, FF, CR, LF and SPC */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; - for (i = 0; i < BASE64_VALUE_SZ; i++) - base64_value[i] = -1; +#define TABLE_INVALID -1 +#define TABLE_SPACE -2 +#define TABLE_END -3 - for (i = 0; i < 64; i++) - base64_value[(int) base64_code[i]] = i; - base64_value['='] = 0; +#define BASE64_VALUE_SZ 256 +int base64_value[BASE64_VALUE_SZ]; - base64_initialized = 1; +void +base64_decode_init(struct base64_decode_ctx *ctx) +{ + ctx->word = ctx->bits = ctx->padding = 0; } -int -base64_decode_len(const char *data) +static int +base64_decode_single(struct base64_decode_ctx *ctx, uint8_t *dst, uint8_t src) { - if (!data || !*data) + int data = decode_table[src]; + + switch(data) { + default: + assert(data >= 0 && data < 0x40); + + if (ctx->padding) + return -1; + + ctx->word = ctx->word << 6 | data; + ctx->bits += 6; + + if (ctx->bits >= 8) { + ctx->bits -= 8; + dst[0] = ctx->word >> ctx->bits; + return 1; + } else + return 0; + + case TABLE_INVALID: + return -1; + + case TABLE_SPACE: return 0; - int terminatorLen = 0; - int dataLen = strlen(data); - int i; + case TABLE_END: + /* There can be at most two padding characters. */ + if (!ctx->bits || ctx->padding > 2) + return -1; - for (i = dataLen - 1; i >= 0; i--) { - if (data[i] == '=') - terminatorLen++; - if (data[i] != '=') - break; + if (ctx->word & ( (1<bits) - 1)) + /* We shouldn't have any leftover bits */ + return -1; + + ctx->padding++; + ctx->bits -= 2; + return 0; } - return dataLen / 4 * 3 - terminatorLen; } int -base64_decode(char *result, unsigned int result_size, const char *p) +base64_decode_update(struct base64_decode_ctx *ctx, + size_t *dst_length, + uint8_t *dst, + size_t src_length, + const uint8_t *src) { - int j = 0; - int c; - long val; - if (!p || !result || result_size == 0) - return j; - if (!base64_initialized) - base64_init(); - val = c = 0; - for (; *p; p++) { - unsigned int k = ((unsigned char) *p) % BASE64_VALUE_SZ; - if (base64_value[k] < 0) - continue; - val <<= 6; - val += base64_value[k]; - if (++c < 4) - continue; - /* One quantum of four encoding characters/24 bit */ - if (j+4 <= result_size) { - // Speed optimization: plenty of space, avoid some per-byte checks. - result[j++] = (val >> 16) & 0xff; /* High 8 bits */ - result[j++] = (val >> 8) & 0xff; /* Mid 8 bits */ - result[j++] = val & 0xff; /* Low 8 bits */ - } else { - // part-quantum goes a bit slower with per-byte checks - result[j++] = (val >> 16) & 0xff; /* High 8 bits */ - if (j == result_size) - return j; - result[j++] = (val >> 8) & 0xff; /* Mid 8 bits */ - if (j == result_size) - return j; - result[j++] = val & 0xff; /* Low 8 bits */ + size_t done; + size_t i; + + for (i = 0, done = 0; i < src_length; i++) { + switch(base64_decode_single(ctx, dst + done, src[i])) { + case -1: + return 0; + case 1: + done++; + /* Fall through */ + case 0: + break; + default: + abort(); } - if (j == result_size) - return j; - val = c = 0; } - return j; + + assert(done <= BASE64_DECODE_LENGTH(src_length)); + + *dst_length = done; + return 1; } int -base64_encode_len(int len) +base64_decode_final(struct base64_decode_ctx *ctx) { - // NP: some magic numbers + potential nil-terminator - return ((len + 2) / 3 * 4) + 1; + return ctx->bits == 0; } -const char * -old_base64_encode(const char *decoded_str) +static void +base64_encode_raw(uint8_t *dst, size_t length, const uint8_t *src) { - static char result[BASE64_RESULT_SZ]; - base64_encode_str(result, sizeof(result), decoded_str, strlen(decoded_str)); - return result; + const uint8_t *in = src + length; + uint8_t *out = dst + BASE64_ENCODE_RAW_LENGTH(length); + + unsigned left_over = length % 3; + + if (left_over) { + in -= left_over; + *--out = '='; + switch(left_over) { + case 1: + *--out = '='; + *--out = ENCODE(in[0] << 4); + break; + + case 2: + *--out = ENCODE( in[1] << 2); + *--out = ENCODE((in[0] << 4) | (in[1] >> 4)); + break; + + default: + abort(); + } + *--out = ENCODE(in[0] >> 2); + } + + while (in > src) { + in -= 3; + *--out = ENCODE( in[2]); + *--out = ENCODE((in[1] << 2) | (in[2] >> 6)); + *--out = ENCODE((in[0] << 4) | (in[1] >> 4)); + *--out = ENCODE( in[0] >> 2); + } + assert(in == src); + assert(out == dst); } -const char * -base64_encode_bin(const char *decoded_str, int len) +void +base64_encode_init(struct base64_encode_ctx *ctx) { - static char result[BASE64_RESULT_SZ]; - base64_encode_str(result, sizeof(result), decoded_str, len); - return result; + ctx->word = ctx->bits = 0; } -int -base64_encode_str(char *result, int result_max_size, const char *data, int data_size) +/* Encodes a single byte. */ +size_t +base64_encode_single(struct base64_encode_ctx *ctx, + uint8_t *dst, + uint8_t src) { - if (result_max_size < 1) - return 0; + unsigned done = 0; + unsigned word = ctx->word << 8 | src; + unsigned bits = ctx->bits + 8; - int used = base64_encode(result, result_max_size, data, data_size); - /* terminate */ - if (used >= result_max_size) { - result[result_max_size - 1] = '\0'; - return result_max_size; - } else { - result[used++] = '\0'; + while (bits >= 6) { + bits -= 6; + dst[done++] = ENCODE(word >> bits); } - return used; + + ctx->bits = bits; + ctx->word = word; + + assert(done <= 2); + + return done; } -/* adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments */ -int -base64_encode(char *result, int result_size, const char *data, int data_size) +/* Returns the number of output characters. DST should point to an + * area of size at least BASE64_ENCODE_LENGTH(length). */ +size_t +base64_encode_update(struct base64_encode_ctx *ctx, + uint8_t *dst, + size_t length, + const uint8_t *src) { - int bits = 0; - int char_count = 0; - int out_cnt = 0; + size_t done = 0; + size_t left = length; + unsigned left_over; + size_t bulk; - if (!data || !*data || !result || result_size < 1 || data_size < 1) - return 0; + while (ctx->bits && left) { + left--; + done += base64_encode_single(ctx, dst + done, *src++); + } - if (!base64_initialized) - base64_init(); - - while (data_size--) { - int c = (unsigned char) *data++; - bits += c; - char_count++; - if (char_count == 3) { - if (out_cnt >= result_size) - break; - if (out_cnt+4 <= result_size) { - result[out_cnt++] = base64_code[bits >> 18]; - result[out_cnt++] = base64_code[(bits >> 12) & 0x3f]; - result[out_cnt++] = base64_code[(bits >> 6) & 0x3f]; - result[out_cnt++] = base64_code[bits & 0x3f]; - } else { - // part-quantum goes a bit slower with per-byte checks - result[out_cnt++] = base64_code[bits >> 18]; - if (out_cnt >= result_size) - break; - result[out_cnt++] = base64_code[(bits >> 12) & 0x3f]; - if (out_cnt >= result_size) - break; - result[out_cnt++] = base64_code[(bits >> 6) & 0x3f]; - if (out_cnt >= result_size) - break; - result[out_cnt++] = base64_code[bits & 0x3f]; - } - bits = 0; - char_count = 0; - } else { - bits <<= 8; - } + left_over = left % 3; + bulk = left - left_over; + + if (bulk) { + assert(!(bulk % 3)); + + base64_encode_raw(dst + done, bulk, src); + done += BASE64_ENCODE_RAW_LENGTH(bulk); + src += bulk; + left = left_over; } - if (char_count != 0) { - bits <<= 16 - (8 * char_count); - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = base64_code[bits >> 18]; - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = base64_code[(bits >> 12) & 0x3f]; - if (char_count == 1) { - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = '='; - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = '='; - } else { - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = base64_code[(bits >> 6) & 0x3f]; - if (out_cnt >= result_size) - return result_size; - result[out_cnt++] = '='; - } + + while (left) { + left--; + done += base64_encode_single(ctx, dst + done, *src++); } - return (out_cnt >= result_size?result_size:out_cnt); + + assert(done <= BASE64_ENCODE_LENGTH(length)); + + return done; } +/* DST should point to an area of size at least + * BASE64_ENCODE_FINAL_SIZE */ +size_t +base64_encode_final(struct base64_encode_ctx *ctx, + uint8_t *dst) +{ + unsigned done = 0; + unsigned bits = ctx->bits; + + if (bits) { + dst[done++] = ENCODE(ctx->word << (6 - ctx->bits)); + for (; bits < 6; bits += 2) + dst[done++] = '='; + + ctx->bits = 0; + } + + assert(done <= BASE64_ENCODE_FINAL_LENGTH); + return done; +} + +#endif /* HAVE_NETTLE_BASE64_H */ + diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index 4d24035a8c..9a8b92e8a0 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -1526,7 +1526,13 @@ HttpHeader::getAuth(http_hdr_type id, const char *auth_scheme) const return NULL; static char decodedAuthToken[8192]; - const int decodedLen = base64_decode(decodedAuthToken, sizeof(decodedAuthToken)-1, field); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t decodedLen = 0; + if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast(decodedAuthToken), strlen(field), reinterpret_cast(field)) || + !base64_decode_final(&ctx)) { + return NULL; + } decodedAuthToken[decodedLen] = '\0'; return decodedAuthToken; } diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc index 51c108a8a1..f85092e754 100644 --- a/src/adaptation/icap/ModXact.cc +++ b/src/adaptation/icap/ModXact.cc @@ -1360,11 +1360,14 @@ void Adaptation::Icap::ModXact::makeRequestHeaders(MemBuf &buf) String vh=virgin.header->header.getByName("Proxy-Authorization"); buf.Printf("Proxy-Authorization: " SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(vh)); } else if (request->extacl_user.size() > 0 && request->extacl_passwd.size() > 0) { - char loginbuf[256]; - snprintf(loginbuf, sizeof(loginbuf), SQUIDSTRINGPH ":" SQUIDSTRINGPH, - SQUIDSTRINGPRINT(request->extacl_user), - SQUIDSTRINGPRINT(request->extacl_passwd)); - buf.Printf("Proxy-Authorization: Basic %s\r\n", old_base64_encode(loginbuf)); + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + uint8_t base64buf[base64_encode_len(MAX_LOGIN_SZ)]; + size_t resultLen = base64_encode_update(&ctx, base64buf, request->extacl_user.size(), reinterpret_cast(request->extacl_user.rawBuf())); + resultLen += base64_encode_update(&ctx, base64buf+resultLen, 1, reinterpret_cast(":")); + resultLen += base64_encode_update(&ctx, base64buf+resultLen, request->extacl_passwd.size(), reinterpret_cast(request->extacl_passwd.rawBuf())); + resultLen += base64_encode_final(&ctx, base64buf+resultLen); + buf.Printf("Proxy-Authorization: Basic %.*s\r\n", resultLen, base64buf); } // share the cross-transactional database records if needed @@ -1510,15 +1513,24 @@ void Adaptation::Icap::ModXact::makeAllowHeader(MemBuf &buf) void Adaptation::Icap::ModXact::makeUsernameHeader(const HttpRequest *request, MemBuf &buf) { #if USE_AUTH + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + + const char *value = NULL; if (request->auth_user_request != NULL) { - char const *name = request->auth_user_request->username(); - if (name) { - const char *value = TheConfig.client_username_encode ? old_base64_encode(name) : name; - buf.Printf("%s: %s\r\n", TheConfig.client_username_header, value); - } + value = request->auth_user_request->username(); } else if (request->extacl_user.size() > 0) { - const char *value = TheConfig.client_username_encode ? old_base64_encode(request->extacl_user.termedBuf()) : request->extacl_user.termedBuf(); - buf.Printf("%s: %s\r\n", TheConfig.client_username_header, value); + value = request->extacl_user.termedBuf(); + } + + if (value) { + if (TheConfig.client_username_encode) { + uint8_t base64buf[base64_encode_len(MAX_LOGIN_SZ)]; + size_t resultLen = base64_encode_update(&ctx, base64buf, strlen(value), reinterpret_cast(value)); + resultLen += base64_encode_final(&ctx, base64buf+resultLen); + buf.Printf("%s: %.*s\r\n", TheConfig.client_username_header, resultLen, base64buf); + } else + buf.Printf("%s: %s\r\n", TheConfig.client_username_header, value); } #endif } diff --git a/src/auth/digest/Config.cc b/src/auth/digest/Config.cc index 5e08a3847d..efee47df3c 100644 --- a/src/auth/digest/Config.cc +++ b/src/auth/digest/Config.cc @@ -102,7 +102,11 @@ authDigestNonceEncode(digest_nonce_h * nonce) if (nonce->key) xfree(nonce->key); - nonce->key = xstrdup(base64_encode_bin((char *) &(nonce->noncedata), sizeof(digest_nonce_data))); + nonce->key = xcalloc(base64_encode_len(sizeof(digest_nonce_data)), 1); + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, reinterpret_cast(nonce->key), sizeof(digest_nonce_data), reinterpret_cast(&(nonce->noncedata))); + blen += base64_encode_final(&ctx, reinterpret_cast(nonce->key)+blen); } digest_nonce_h * diff --git a/src/http.cc b/src/http.cc index a4250c0a11..75dd9a995a 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1608,9 +1608,13 @@ httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHe } } + uint8_t loginbuf[base64_encode_len(MAX_LOGIN_SZ)]; + size_t blen; + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + /* Special mode to pass the username to the upstream cache */ if (*request->peer_login == '*') { - char loginbuf[256]; const char *username = "-"; if (request->extacl_user.size()) @@ -1620,10 +1624,10 @@ httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHe username = request->auth_user_request->username(); #endif - snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, request->peer_login + 1); - - httpHeaderPutStrf(hdr_out, header, "Basic %s", - old_base64_encode(loginbuf)); + blen = base64_encode_update(&ctx, loginbuf, strlen(username), reinterpret_cast(username)); + blen += base64_encode_update(&ctx, loginbuf+blen, strlen(request->peer_login +1), reinterpret_cast(request->peer_login +1)); + blen += base64_encode_final(&ctx, loginbuf+blen); + httpHeaderPutStrf(hdr_out, header, "Basic %.*s", blen, loginbuf); return; } @@ -1631,12 +1635,12 @@ httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHe if (request->extacl_user.size() && request->extacl_passwd.size() && (strcmp(request->peer_login, "PASS") == 0 || strcmp(request->peer_login, "PROXYPASS") == 0)) { - char loginbuf[256]; - snprintf(loginbuf, sizeof(loginbuf), SQUIDSTRINGPH ":" SQUIDSTRINGPH, - SQUIDSTRINGPRINT(request->extacl_user), - SQUIDSTRINGPRINT(request->extacl_passwd)); - httpHeaderPutStrf(hdr_out, header, "Basic %s", - old_base64_encode(loginbuf)); + + blen = base64_encode_update(&ctx, loginbuf, request->extacl_user.size(), reinterpret_cast(request->extacl_user.rawBuf())); + blen += base64_encode_update(&ctx, loginbuf+blen, 1, reinterpret_cast(":")); + blen += base64_encode_update(&ctx, loginbuf+blen, request->extacl_passwd.size(), reinterpret_cast(request->extacl_passwd.rawBuf())); + blen += base64_encode_final(&ctx, loginbuf+blen); + httpHeaderPutStrf(hdr_out, header, "Basic %.*s", blen, loginbuf); return; } // if no external user credentials are available to fake authentication with PASS acts like PASSTHRU @@ -1659,8 +1663,9 @@ httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHe } #endif /* HAVE_KRB5 && HAVE_GSSAPI */ - httpHeaderPutStrf(hdr_out, header, "Basic %s", - old_base64_encode(request->peer_login)); + blen = base64_encode_update(&ctx, loginbuf, strlen(request->peer_login), reinterpret_cast(request->peer_login)); + blen += base64_encode_final(&ctx, loginbuf+blen); + httpHeaderPutStrf(hdr_out, header, "Basic %.*s", blen, loginbuf); return; } @@ -1796,9 +1801,14 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request, /* append Authorization if known in URL, not in header and going direct */ if (!hdr_out->has(HDR_AUTHORIZATION)) { if (!request->flags.proxying && !request->url.userInfo().isEmpty()) { - static char result[MAX_URL*2]; // should be big enough for a single URI segment - if (base64_encode_str(result, sizeof(result)-1, request->url.userInfo().rawContent(), request->url.userInfo().length()) < static_cast(sizeof(result)-1)) - httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", result); + static uint8_t result[base64_encode_len(MAX_URL*2)]; // should be big enough for a single URI segment + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, result, request->url.userInfo().length(), reinterpret_cast(request->url.userInfo().rawContent())); + blen += base64_encode_final(&ctx, result+blen); + result[blen] = '\0'; + if (blen) + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %.*s", blen, result); } } diff --git a/src/peer_proxy_negotiate_auth.cc b/src/peer_proxy_negotiate_auth.cc index 253c64cec1..45f2248491 100644 --- a/src/peer_proxy_negotiate_auth.cc +++ b/src/peer_proxy_negotiate_auth.cc @@ -553,10 +553,14 @@ char *peer_proxy_negotiate_auth(char *principal_name, char *proxy) { debugs(11, 5, HERE << "Got token with length " << output_token.length); if (output_token.length) { - - token = - (char *) base64_encode_bin((const char *) output_token.value, - output_token.length); + static uint8_t b64buf[8192]; // XXX: 8KB only because base64_encode_bin() used to. + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, b64buf, output_token.length, reinterpret_cast(output_token.value)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[blen] = '\0'; + + token = reinterpret_cast(b64buf); } cleanup: diff --git a/tools/cachemgr.cc b/tools/cachemgr.cc index 2073d5c0d8..49b5ed081c 100644 --- a/tools/cachemgr.cc +++ b/tools/cachemgr.cc @@ -1077,7 +1077,11 @@ make_pub_auth(cachemgr_request * req) const int encodedLen = base64_encode_len(bufLen); req->pub_auth = (char *) xmalloc(encodedLen); - base64_encode_str(req->pub_auth, encodedLen, buf, bufLen); + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, reinterpret_cast(req->pub_auth), bufLen, reinterpret_cast(buf)); + blen += base64_encode_final(&ctx, reinterpret_cast(req->pub_auth)+blen); + req->pub_auth[blen] = '\0'; debug("cmgr: encoded: '%s'\n", req->pub_auth); } @@ -1096,9 +1100,16 @@ decode_pub_auth(cachemgr_request * req) if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname))) return; - const int decodedLen = base64_decode_len(req->pub_auth); + size_t decodedLen = BASE64_DECODE_LENGTH(strlen(req->pub_auth)); buf = (char*)xmalloc(decodedLen); - base64_decode(buf, decodedLen, req->pub_auth); + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + base64_decode_update(&ctx, &decodedLen, reinterpret_cast(buf), strlen(req->pub_auth), reinterpret_cast(req->pub_auth)); + if (!base64_decode_final(&ctx)) { + debug("cmgr: base64 decode failure. Incomplete auth token string.\n"); + xfree(buf); + return; + } debug("cmgr: length ok\n"); @@ -1178,14 +1189,18 @@ make_auth_header(const cachemgr_request * req) if (encodedLen <= 0) return ""; - char *str64 = static_cast(xmalloc(encodedLen)); - base64_encode_str(str64, encodedLen, buf, bufLen); + uint8_t *str64 = static_cast(xmalloc(encodedLen)); + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, str64, bufLen, reinterpret_cast(buf)); + blen += base64_encode_final(&ctx, str64+blen); + str64[blen] = '\0'; - stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64); + stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n", blen, str64); assert(stringLength < sizeof(buf)); - snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %s\r\n", str64); + snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %.*s\r\n", blen, str64); xfree(str64); return buf; diff --git a/tools/squidclient/gssapi_support.cc b/tools/squidclient/gssapi_support.cc index 5498bb7885..5e28cc80a9 100644 --- a/tools/squidclient/gssapi_support.cc +++ b/tools/squidclient/gssapi_support.cc @@ -102,7 +102,10 @@ GSSAPI_token(const char *server) if (!server) { std::cerr << "ERROR: GSSAPI: No server name" << std::endl; - return (char *)"ERROR"; + token = new char[6]; + memcpy(token, "ERROR", 5); + token[5] = '\0'; + return token; } service.value = xmalloc(strlen("HTTP") + strlen(server) + 2); snprintf((char *) service.value, strlen("HTTP") + strlen(server) + 2, "%s@%s", "HTTP", server); @@ -127,15 +130,24 @@ GSSAPI_token(const char *server) NULL, NULL); - if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()")) { + if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()") && output_token.length) { + uint8_t *b64buf = new uint8_t[base64_encode_len(output_token.length)]; + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, b64buf, output_token.length, reinterpret_cast(output_token.value)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[blen] = '\0'; - if (output_token.length) - token = (char *) base64_encode_bin((const char *) output_token.value, output_token.length); + token = reinterpret_cast(b64buf); } } - if (!output_token.length) - token = (char *) "ERROR"; + if (!output_token.length) { + token = new char[6]; + memcpy(token, "ERROR", 5); + token[5] = '\0'; + } + gss_delete_sec_context(&minor_status, &gss_context, NULL); gss_release_buffer(&minor_status, &service); gss_release_buffer(&minor_status, &input_token); diff --git a/tools/squidclient/squidclient.cc b/tools/squidclient/squidclient.cc index 0f72bd8604..54ded3df33 100644 --- a/tools/squidclient/squidclient.cc +++ b/tools/squidclient/squidclient.cc @@ -417,6 +417,9 @@ main(int argc, char *argv[]) snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards); strcat(msg, buf); } + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen; if (proxy_user) { const char *user = proxy_user; const char *password = proxy_password; @@ -428,8 +431,11 @@ main(int argc, char *argv[]) std::cerr << "ERROR: Proxy password missing" << std::endl; exit(1); } - snprintf(buf, BUFSIZ, "%s:%s", user, password); - snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", old_base64_encode(buf)); + blen = base64_encode_update(&ctx, reinterpret_cast(buf), strlen(user), reinterpret_cast(user)); + blen += base64_encode_update(&ctx, reinterpret_cast(buf+blen), 1, reinterpret_cast(":")); + blen += base64_encode_update(&ctx, reinterpret_cast(buf+blen), strlen(password), reinterpret_cast(password)); + blen += base64_encode_final(&ctx, reinterpret_cast(buf+blen)); + snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %.*s\r\n", blen, buf); strcat(msg, buf); } if (www_user) { @@ -443,22 +449,29 @@ main(int argc, char *argv[]) std::cerr << "ERROR: WWW password missing" << std::endl; exit(1); } - snprintf(buf, BUFSIZ, "%s:%s", user, password); - snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", old_base64_encode(buf)); + blen = base64_encode_update(&ctx, reinterpret_cast(buf), strlen(user), reinterpret_cast(user)); + blen += base64_encode_update(&ctx, reinterpret_cast(buf+blen), 1, reinterpret_cast(":")); + blen += base64_encode_update(&ctx, reinterpret_cast(buf+blen), strlen(password), reinterpret_cast(password)); + blen += base64_encode_final(&ctx, reinterpret_cast(buf+blen)); + snprintf(buf, BUFSIZ, "Authorization: Basic %.*s\r\n", blen, buf); strcat(msg, buf); } #if HAVE_GSSAPI if (www_neg) { if (host) { - snprintf(buf, BUFSIZ, "Authorization: Negotiate %s\r\n", GSSAPI_token(host)); + const char *token = GSSAPI_token(host); + snprintf(buf, BUFSIZ, "Authorization: Negotiate %s\r\n", token); strcat(msg, buf); + delete token; } else std::cerr << "ERROR: server host missing" << std::endl; } if (proxy_neg) { if (Transport::Config.hostname) { - snprintf(buf, BUFSIZ, "Proxy-Authorization: Negotiate %s\r\n", GSSAPI_token(Transport::Config.hostname)); + const char *token = GSSAPI_token(Transport::Config.hostname); + snprintf(buf, BUFSIZ, "Proxy-Authorization: Negotiate %s\r\n", token); strcat(msg, buf); + delete token; } else std::cerr << "ERROR: proxy server host missing" << std::endl; } -- 2.47.2