From: Miod Vallat Date: Wed, 26 Feb 2025 13:31:50 +0000 (+0100) Subject: Damage control in Lua createForward() and createForward6(). X-Git-Tag: auth-4.9.5~4^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=550150dca3819e9881cc82e55f88b6b5d11f350b;p=thirdparty%2Fpdns.git Damage control in Lua createForward() and createForward6(). - make sure all computed results are passed to a ComboAddress constructor, which will reject ill-formed data. This wasn't the case in createForward, when interpreting part of the requested name as an ipv4 address encoded in hexadecimal (e.g. 7f000001), but the actual name wasn't. This would otherwise end up with a SERVFAIL answer and a Lua stack traceback containing messages such as: Unable to convert presentation address '4294967292.xx.yy.zz' for a name ending with "-4" and six hex digits. - wrap these functions into a try/catch block in order to cope with possible exceptions raised by ComboAddress. This wasn't the case in createForward6 when the requested name contains at least 8 dots - this doesn't imply each component is a valid ipv6 chunk. (cherry picked from commit 97800540ef65f19cd9cb6e2624682c0840a6eafc) --- diff --git a/pdns/lua-record.cc b/pdns/lua-record.cc index d1215b2add..dea4aa1f86 100644 --- a/pdns/lua-record.cc +++ b/pdns/lua-record.cc @@ -915,81 +915,87 @@ static void setupLuaRecords(LuaContext& lua) // NOLINT(readability-function-cogn return std::string("error"); }); lua.writeFunction("createForward", []() { - static string allZerosIP("0.0.0.0"); - DNSName rel=s_lua_record_ctx->qname.makeRelative(s_lua_record_ctx->zone); - // parts is something like ["1", "2", "3", "4", "static"] or - // ["1", "2", "3", "4"] or ["ip40414243", "ip-addresses", ...] - auto parts = rel.getRawLabels(); - // Yes, this still breaks if an 1-2-3-4.XXXX is nested too deeply... - if(parts.size()>=4) { - try { - ComboAddress ca(parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3]); - return ca.toString(); - } catch (const PDNSException &e) { - return allZerosIP; + static string allZerosIP{"0.0.0.0"}; + try { + DNSName rel{s_lua_record_ctx->qname.makeRelative(s_lua_record_ctx->zone)}; + + // parts is something like ["1", "2", "3", "4", "static"] or + // ["1", "2", "3", "4"] or ["ip40414243", "ip-addresses", ...] + auto parts = rel.getRawLabels(); + // Yes, this still breaks if an 1-2-3-4.XXXX is nested too deeply... + if (parts.size() >= 4) { + ComboAddress address(parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3]); + return address.toString(); } - } else if (!parts.empty()) { - auto& input = parts.at(0); + if (!parts.empty()) { + auto& input = parts.at(0); - // allow a word without - in front, as long as it does not contain anything that could be a number - size_t nonhexprefix = strcspn(input.c_str(), "0123456789abcdefABCDEF"); - if (nonhexprefix > 0) { - input = input.substr(nonhexprefix); - } + // allow a word without - in front, as long as it does not contain anything that could be a number + size_t nonhexprefix = strcspn(input.c_str(), "0123456789abcdefABCDEF"); + if (nonhexprefix > 0) { + input = input.substr(nonhexprefix); + } - // either hex string, or 12-13-14-15 - vector ip_parts; - - stringtok(ip_parts, input, "-"); - unsigned int x1, x2, x3, x4; - if (ip_parts.size() >= 4) { - // 1-2-3-4 with any prefix (e.g. ip-foo-bar-1-2-3-4) - string ret; - for (size_t index=4; index > 0; index--) { - auto octet = ip_parts[ip_parts.size() - index]; - try { - auto octetVal = std::stol(octet); + // either hex string, or 12-13-14-15 + vector ip_parts; + + stringtok(ip_parts, input, "-"); + if (ip_parts.size() >= 4) { + // 1-2-3-4 with any prefix (e.g. ip-foo-bar-1-2-3-4) + string ret; + for (size_t index=4; index > 0; index--) { + auto octet = ip_parts.at(ip_parts.size() - index); + auto octetVal = std::stol(octet); // may throw if (octetVal >= 0 && octetVal <= 255) { - ret += ip_parts.at(ip_parts.size() - index) + "."; + ret += octet + "."; } else { return allZerosIP; } - } catch (const std::exception &e) { - return allZerosIP; } + ret.resize(ret.size() - 1); // remove trailing dot after last octet + return ret; } - ret.resize(ret.size() - 1); // remove trailing dot after last octet - return ret; - } - if(input.length() >= 8) { - auto last8 = input.substr(input.length()-8); - if(sscanf(last8.c_str(), "%02x%02x%02x%02x", &x1, &x2, &x3, &x4)==4) { - return std::to_string(x1) + "." + std::to_string(x2) + "." + std::to_string(x3) + "." + std::to_string(x4); + if (input.length() >= 8) { + auto last8 = input.substr(input.length()-8); + unsigned int part1{0}; + unsigned int part2{0}; + unsigned int part3{0}; + unsigned int part4{0}; + if (sscanf(last8.c_str(), "%02x%02x%02x%02x", &part1, &part2, &part3, &part4) == 4) { + ComboAddress address(std::to_string(part1) + "." + std::to_string(part2) + "." + std::to_string(part3) + "." + std::to_string(part4)); + return address.toString(); + } } } + return allZerosIP; + } catch (const PDNSException &e) { + return allZerosIP; } - return allZerosIP; }); lua.writeFunction("createForward6", []() { - DNSName rel=s_lua_record_ctx->qname.makeRelative(s_lua_record_ctx->zone); - auto parts = rel.getRawLabels(); - if(parts.size()==8) { - string tot; - for(int i=0; i<8; ++i) { - if(i) - tot.append(1,':'); - tot+=parts[i]; + static string allZerosIP{"::"}; + try { + DNSName rel{s_lua_record_ctx->qname.makeRelative(s_lua_record_ctx->zone)}; + + auto parts = rel.getRawLabels(); + if (parts.size() == 8) { + string tot; + for (int chunk = 0; chunk < 8; ++chunk) { + if (chunk != 0) { + tot.append(1, ':'); + } + tot += parts.at(chunk); + } + ComboAddress address(tot); + return address.toString(); } - ComboAddress ca(tot); - return ca.toString(); - } - else if(parts.size()==1) { - if (parts[0].find('-') != std::string::npos) { - boost::replace_all(parts[0],"-",":"); - ComboAddress ca(parts[0]); - return ca.toString(); - } else { + if (parts.size() == 1) { + if (parts[0].find('-') != std::string::npos) { + std::replace(parts[0].begin(), parts[0].end(), '-', ':'); + ComboAddress address(parts[0]); + return address.toString(); + } if (parts[0].size() >= 32) { auto ippart = parts[0].substr(parts[0].size()-32); auto fulladdress = @@ -1002,57 +1008,63 @@ static void setupLuaRecords(LuaContext& lua) // NOLINT(readability-function-cogn ippart.substr(24, 4) + ":" + ippart.substr(28, 4); - ComboAddress ca(fulladdress); - return ca.toString(); + ComboAddress address(fulladdress); + return address.toString(); } } + return allZerosIP; + } catch (const PDNSException &e) { + return allZerosIP; } - - return std::string("::"); }); - lua.writeFunction("createReverse6", [](string format, boost::optional> e){ + lua.writeFunction("createReverse6", [](const string &format, boost::optional> excp){ vector candidates; try { auto labels= s_lua_record_ctx->qname.getRawLabels(); - if(labels.size()<32) + if (labels.size()<32) { return std::string("unknown"); + } boost::format fmt(format); fmt.exceptions( boost::io::all_error_bits ^ ( boost::io::too_many_args_bit | boost::io::too_few_args_bit ) ); string together; vector quads; - for(int i=0; i<8; ++i) { - if(i) - together+=":"; + for (int chunk = 0; chunk < 8; ++chunk) { + if (chunk != 0) { + together += ":"; + } string lquad; - for(int j=0; j <4; ++j) { - lquad.append(1, labels[31-i*4-j][0]); - together += labels[31-i*4-j][0]; + for (int quartet = 0; quartet < 4; ++quartet) { + lquad.append(1, labels[31 - chunk * 4 - quartet][0]); + together += labels[31 - chunk * 4 - quartet][0]; } quads.push_back(lquad); } - ComboAddress ip6(together,0); + ComboAddress ip6(together,0); - if(e) { - auto& addrs=*e; + if (excp) { + auto& addrs=*excp; for(const auto& addr: addrs) { // this makes sure we catch all forms of the address - if(ComboAddress(addr.first,0)==ip6) + if (ComboAddress(addr.first, 0) == ip6) { return addr.second; + } } } string dashed=ip6.toString(); boost::replace_all(dashed, ":", "-"); - for(int i=31; i>=0; --i) - fmt % labels[i]; + for (int byte = 31; byte >= 0; --byte) { + fmt % labels[byte]; + } fmt % dashed; - for(const auto& lquad : quads) + for(const auto& lquad : quads) { fmt % lquad; + } return fmt.str(); } diff --git a/regression-tests.auth-py/test_LuaRecords.py b/regression-tests.auth-py/test_LuaRecords.py index 973d98568c..8c383035ec 100644 --- a/regression-tests.auth-py/test_LuaRecords.py +++ b/regression-tests.auth-py/test_LuaRecords.py @@ -1014,6 +1014,8 @@ createforward6.example.org. 3600 IN NS ns2.example.org. "ip40414243": "64.65.66.67", "ipp40414243": "64.65.66.67", "ip4041424": "0.0.0.0", + "ip-441424": "0.0.0.0", + "ip-5abcdef": "0.0.0.0", "host64-22-33-44": "64.22.33.44", "2.2.2.2": "0.0.0.0" # filtered }), @@ -1025,7 +1027,8 @@ createforward6.example.org. 3600 IN NS ns2.example.org. "2001--db8" : "2001::db8", "20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8", "blabla20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8", - "4000-db8--1" : "fe80::1" # filtered, with fallback address override + "4000-db8--1" : "fe80::1", # filtered, with fallback address override + "l1.l2.l3.l4.l5.l6.l7.l8" : "fe80::1" }), ".createreverse6.example.org." : (dns.rdatatype.PTR, { "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2" : "2001--db8.example.com.",