/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #include "squid.h" #include "base/TextException.h" #include "sbuf/Stream.h" #include "ssl/crtd_message.h" #include "ssl/gadgets.h" #include #include #include Ssl::CrtdMessage::CrtdMessage(MessageKind kind) : body_size(0), state(kind == REPLY ? BEFORE_LENGTH: BEFORE_CODE) {} Ssl::CrtdMessage::ParseResult Ssl::CrtdMessage::parse(const char * buffer, size_t len) { char const *current_pos = buffer; while (current_pos != buffer + len && state != END) { switch (state) { case BEFORE_CODE: { if (xisspace(*current_pos)) { ++current_pos; break; } if (xisalpha(*current_pos)) { state = CODE; break; } clear(); return ERROR; } case CODE: { if (xisalnum(*current_pos) || *current_pos == '_') { current_block += *current_pos; ++current_pos; break; } if (xisspace(*current_pos)) { code = current_block; current_block.clear(); state = BEFORE_LENGTH; break; } clear(); return ERROR; } case BEFORE_LENGTH: { if (xisspace(*current_pos)) { ++current_pos; break; } if (xisdigit(*current_pos)) { state = LENGTH; break; } clear(); return ERROR; } case LENGTH: { if (xisdigit(*current_pos)) { current_block += *current_pos; ++current_pos; break; } if (xisspace(*current_pos)) { body_size = atoi(current_block.c_str()); current_block.clear(); state = BEFORE_BODY; break; } clear(); return ERROR; } case BEFORE_BODY: { if (body_size == 0) { state = END; break; } if (xisspace(*current_pos)) { ++current_pos; break; } else { state = BODY; break; } } case BODY: { size_t body_len = (static_cast(buffer + len - current_pos) >= body_size - current_block.length()) ? body_size - current_block.length() : static_cast(buffer + len - current_pos); current_block += std::string(current_pos, body_len); current_pos += body_len; if (current_block.length() == body_size) { body = current_block; state = END; } if (current_block.length() > body_size) { clear(); return ERROR; } break; } case END: { return OK; } } } if (state != END) return INCOMPLETE; return OK; } std::string const & Ssl::CrtdMessage::getBody() const { return body; } std::string const & Ssl::CrtdMessage::getCode() const { return code; } void Ssl::CrtdMessage::setBody(std::string const & aBody) { body = aBody; } void Ssl::CrtdMessage::setCode(std::string const & aCode) { code = aCode; } std::string Ssl::CrtdMessage::compose() const { if (code.empty()) return std::string(); char buffer[10]; snprintf(buffer, sizeof(buffer), "%zd", body.length()); return code + ' ' + buffer + ' ' + body; } void Ssl::CrtdMessage::clear() { body_size = 0; state = BEFORE_CODE; body.clear(); code.clear(); current_block.clear(); } void Ssl::CrtdMessage::parseBody(CrtdMessage::BodyParams & map, std::string & other_part) const { other_part.clear(); // Copy string for using it as temp buffer. std::string temp_body(body.c_str(), body.length()); char * buffer = const_cast(temp_body.c_str()); char * token = strtok(buffer, "\r\n"); while (token != nullptr) { std::string current_string(token); size_t equal_pos = current_string.find('='); if (equal_pos == std::string::npos) { size_t offset_body_part = token - temp_body.c_str(); other_part = std::string(body.c_str() + offset_body_part, body.length() - offset_body_part); break; } else { std::string param(current_string.c_str(), current_string.c_str() + equal_pos); std::string value(current_string.c_str() + equal_pos + 1); map.insert(std::make_pair(param, value)); } token = strtok(nullptr, "\r\n"); } } void Ssl::CrtdMessage::composeBody(CrtdMessage::BodyParams const & map, std::string const & other_part) { body.clear(); for (BodyParams::const_iterator i = map.begin(); i != map.end(); ++i) { if (i != map.begin()) body += "\n"; body += i->first + "=" + i->second; } if (!other_part.empty()) body += '\n' + other_part; } void Ssl::CrtdMessage::parseRequest(CertificateProperties &certProperties) { Ssl::CrtdMessage::BodyParams map; std::string certs_part; parseBody(map, certs_part); Ssl::CrtdMessage::BodyParams::iterator i = map.find(Ssl::CrtdMessage::param_host); if (i == map.end()) { throw TextException("Cannot find \"host\" parameter in request message", Here()); } certProperties.commonName = i->second; i = map.find(Ssl::CrtdMessage::param_SetValidAfter); if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0) certProperties.setValidAfter = true; i = map.find(Ssl::CrtdMessage::param_SetValidBefore); if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0) certProperties.setValidBefore = true; i = map.find(Ssl::CrtdMessage::param_SetCommonName); if (i != map.end()) { // use this as Common Name instead of the hostname // defined with host or Common Name from mimic cert certProperties.commonName = i->second; certProperties.setCommonName = true; } i = map.find(Ssl::CrtdMessage::param_Sign); if (i != map.end()) { if ((certProperties.signAlgorithm = Ssl::certSignAlgorithmId(i->second.c_str())) == Ssl::algSignEnd) { throw TextException(ToSBuf("Wrong signing algorithm: ", i->second), Here()); } } else certProperties.signAlgorithm = Ssl::algSignTrusted; i = map.find(Ssl::CrtdMessage::param_SignHash); const char *signHashName = i != map.end() ? i->second.c_str() : SQUID_SSL_SIGN_HASH_IF_NONE; if (!(certProperties.signHash = EVP_get_digestbyname(signHashName))) { throw TextException(ToSBuf("Wrong signing hash: ", signHashName), Here()); } if (!Ssl::readCertAndPrivateKeyFromMemory(certProperties.signWithX509, certProperties.signWithPkey, certs_part.c_str())) { throw TextException("Broken signing certificate!", Here()); } static const std::string CERT_BEGIN_STR("-----BEGIN CERTIFICATE"); size_t pos; if ((pos = certs_part.find(CERT_BEGIN_STR)) != std::string::npos) { pos += CERT_BEGIN_STR.length(); if ((pos= certs_part.find(CERT_BEGIN_STR, pos)) != std::string::npos) certProperties.mimicCert = ReadCertificate(ReadOnlyBioTiedTo(certs_part.c_str() + pos)); } } void Ssl::CrtdMessage::composeRequest(Ssl::CertificateProperties const &certProperties) { body.clear(); body = Ssl::CrtdMessage::param_host + "=" + certProperties.commonName; if (certProperties.setCommonName) body += "\n" + Ssl::CrtdMessage::param_SetCommonName + "=" + certProperties.commonName; if (certProperties.setValidAfter) body += "\n" + Ssl::CrtdMessage::param_SetValidAfter + "=on"; if (certProperties.setValidBefore) body += "\n" + Ssl::CrtdMessage::param_SetValidBefore + "=on"; if (certProperties.signAlgorithm != Ssl::algSignEnd) body += "\n" + Ssl::CrtdMessage::param_Sign + "=" + certSignAlgorithm(certProperties.signAlgorithm); if (certProperties.signHash) body += "\n" + Ssl::CrtdMessage::param_SignHash + "=" + EVP_MD_name(certProperties.signHash); std::string certsPart; if (!Ssl::writeCertAndPrivateKeyToMemory(certProperties.signWithX509, certProperties.signWithPkey, certsPart)) throw TextException("Ssl::writeCertAndPrivateKeyToMemory()", Here()); if (certProperties.mimicCert.get()) { if (!Ssl::appendCertToMemory(certProperties.mimicCert, certsPart)) throw TextException("Ssl::appendCertToMemory()", Here()); } body += "\n" + certsPart; } const std::string Ssl::CrtdMessage::code_new_certificate("new_certificate"); const std::string Ssl::CrtdMessage::param_host("host"); const std::string Ssl::CrtdMessage::param_SetValidAfter(Ssl::CertAdaptAlgorithmStr[algSetValidAfter]); const std::string Ssl::CrtdMessage::param_SetValidBefore(Ssl::CertAdaptAlgorithmStr[algSetValidBefore]); const std::string Ssl::CrtdMessage::param_SetCommonName(Ssl::CertAdaptAlgorithmStr[algSetCommonName]); const std::string Ssl::CrtdMessage::param_Sign("Sign"); const std::string Ssl::CrtdMessage::param_SignHash("SignHash");