diff --git a/README.md b/README.md index f53e7f67..144fe19c 100644 --- a/README.md +++ b/README.md @@ -3209,6 +3209,8 @@ Adds a name to altname stack, first argument is case-insensitive and can be one URI DNSName DNS + IP + IPAddress This function can be called multiple times in a chained fashion. diff --git a/lib/resty/openssl/include/asn1.lua b/lib/resty/openssl/include/asn1.lua index aa77669c..2151abc7 100644 --- a/lib/resty/openssl/include/asn1.lua +++ b/lib/resty/openssl/include/asn1.lua @@ -26,6 +26,8 @@ ffi.cdef [[ int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v); int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v); + + int ASN1_STRING_length(const ASN1_STRING *x); ]] local function declare_asn1_functions(typ, has_ex) diff --git a/lib/resty/openssl/x509/altname.lua b/lib/resty/openssl/x509/altname.lua index 88dcc1e8..34bf9e04 100644 --- a/lib/resty/openssl/x509/altname.lua +++ b/lib/resty/openssl/x509/altname.lua @@ -22,6 +22,25 @@ local dup = stack_lib.dup_of(STACK) local types = altname_macro.types +local AF_INET = 2 +local AF_INET6 = 10 +if ffi.os == "OSX" then + AF_INET6 = 30 +elseif ffi.os == "BSD" then + AF_INET6 = 28 +elseif ffi.os == "Windows" then + AF_INET6 = 23 +end + +ffi.cdef [[ + typedef int socklen_t; + int inet_pton(int af, const char *restrict src, void *restrict dst); + const char *inet_ntop(int af, const void *restrict src, + char *restrict dst, socklen_t size); +]] + +local ip_buffer = ffi.new("unsigned char [46]") -- 46 bytes enough for both string ipv6 and binary ipv6 + -- similar to GENERAL_NAME_print, but returns value instead of print local gn_decode = function(ctx) local typ = ctx.type @@ -42,7 +61,13 @@ local gn_decode = function(ctx) elseif typ == types.URI then v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier)) elseif typ == types.IP then - v = "IP:" + v = asn1_macro.ASN1_STRING_get0_data(ctx.d.iPAddress) + local l = tonumber(C.ASN1_STRING_length(ctx.d.iPAddress)) + if l ~= 4 and l ~= 16 then + error("Unknown IP address type") + end + v = C.inet_ntop(l == 4 and AF_INET or AF_INET6, v, ip_buffer, 46) + v = ffi_str(v) elseif typ == types.RID then v = "RID:" else @@ -119,7 +144,16 @@ local function gn_set(gn, typ, value) return "x509.altname:gn_set: unknown type " .. typ end - if gn_type ~= types.Email and + if gn_type == types.IP then + if C.inet_pton(AF_INET, txt, ip_buffer) == 1 then + txt = ffi_str(ip_buffer, 4) + elseif C.inet_pton(AF_INET6, txt, ip_buffer) == 1 then + txt = ffi_str(ip_buffer, 16) + else + return "x509.altname:gn_set: invalid IP address " .. txt + end + + elseif gn_type ~= types.Email and gn_type ~= types.URI and gn_type ~= types.DNS then return "x509.altname:gn_set: setting type " .. typ .. " is currently not supported" diff --git a/t/fixtures/x509_sans.crt b/t/fixtures/x509_sans.crt deleted file mode 100644 index a70d61a5..00000000 --- a/t/fixtures/x509_sans.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -XUZUUedOW28XZslL9/N8Hdvt90dZNSs= ------END CERTIFICATE----- diff --git a/t/openssl/x509/altname.t b/t/openssl/x509/altname.t index b8ad83a6..a6692d7f 100644 --- a/t/openssl/x509/altname.t +++ b/t/openssl/x509/altname.t @@ -178,17 +178,18 @@ DNS example.com location =/t { content_by_lua_block { local x509 = require("resty.openssl.x509") - --[[ - openssl req -x509 -nodes -newkey rsa:4096 -keyout x509_sans.key -out x509_sans.cer -days 36500 \ - -subj '/OU=EFS File Encryption Certificate/L=EFS/CN=efs' \ - -addext 'extendedKeyUsage=1.3.6.1.4.1.311.10.3.4.1' -addext 'basicConstraints=CA:FALSE' \ - -addext 'subjectAltName=otherName:msUPN;UTF8:sb@sb.local,IP.1:1.2.3.4,DNS:example.com,email:test@test.com,RID:1.2.3.4' - ]]-- - local x = myassert(x509.new(io.open("t/fixtures/x509_sans.crt"):read("*a"))) - local c = myassert(x:get_subject_alt_name()) + local extension = require "resty.openssl.x509.extension" - for k, v in pairs(c) do + local ext, err = myassert(extension.new("subjectAltName", "otherName:msUPN;UTF8:sb@sb.local,IP.1:255.255.255.255,IP.2:1111:1111:1111:1111:1111:1111:1111:1111,DNS:example.com,email:test@test.com,RID:1.2.3.4")) + + local c = x509.new() + + myassert(c:add_extension(ext)) + + local alts = myassert(c:get_subject_alt_name()) + + for k, v in pairs(alts) do ngx.say(k, ":", v) end } @@ -197,9 +198,41 @@ DNS example.com GET /t --- response_body OtherName:OtherName: -IP:IP: +IP:255.255.255.255 +IP:1111:1111:1111:1111:1111:1111:1111:1111 DNS:example.com email:test@test.com RID:RID: --- no_error_log +[error] + +=== TEST 8: IP addresses are validated and parsed +--- http_config eval: $::HttpConfig +--- config + location =/t { + content_by_lua_block { + local altname = require("resty.openssl.x509.altname") + local c = myassert(altname.new()) + + myassert(c:add("IP", "1.2.3.4")) + myassert(c:add("IPAddress", "100.100.100.100")) + myassert(c:add("IP", "255.255.255.255")) + myassert(c:add("IP", "::1")) + myassert(c:add("IP", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")) + for _, v in ipairs({"1", ":::", "ffff:", "256.1.1.1"}) do + local _, err = c:add("IP", v) + if err == nil then + ngx.say("should error on " .. v) + end + end + + ngx.say(c:tostring()) + + } + } +--- request + GET /t +--- response_body +IP=1.2.3.4/IP=100.100.100.100/IP=255.255.255.255/IP=::1/IP=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +--- no_error_log [error] \ No newline at end of file