]> granicus.if.org Git - ejabberd/commitdiff
Support IDN hostnames in ACME requests
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Sun, 22 Sep 2019 08:04:38 +0000 (11:04 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Sun, 22 Sep 2019 08:04:38 +0000 (11:04 +0300)
rebar.config
src/ejabberd_acme.erl

index e702cff512a2dd11e38343c97c3ab7f57c8f9e7b..fcf26fc3ba4f79badc965f498d57ade473806901 100644 (file)
@@ -24,7 +24,8 @@
         {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.2"}}},
         {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.17"}}},
         {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.37"}}},
-        {xmpp, ".*", {git, "https://github.com/processone/xmpp", "e3181a5"}},
+       {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}},
+        {xmpp, ".*", {git, "https://github.com/processone/xmpp", "984b3c3"}},
         {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.20"}}},
        {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.0"}}},
         {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
@@ -33,7 +34,7 @@
         {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
         {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.12"}}},
         {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.4"}}},
-       {acme, ".*", {git, "https://github.com/processone/acme.git", "b4c2899"}},
+       {acme, ".*", {git, "https://github.com/processone/acme.git", "b312547"}},
         {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.29"}}}},
         {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.30"}}}},
         {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
index 67a1e969f1b6ce630ab1ba185166cafbce6cd2e6..9dbe22b5808b0f9f7df20be6f90dfce689846fa3 100644 (file)
@@ -46,7 +46,9 @@
 -type cert() :: #'OTPCertificate'{}.
 -type cert_type() :: ec | rsa.
 -type io_error() :: file:posix().
--type issue_result() :: ok | acme:issue_return() | {error, {file, io_error()}}.
+-type issue_result() :: ok | acme:issue_return() |
+                       {error, {file, io_error()} |
+                               {idna_failed, binary()}}.
 
 %%%===================================================================
 %%% API
@@ -191,17 +193,22 @@ unregister_challenge(Ref) ->
 %%%===================================================================
 -spec issue_request(state(), [binary(),...]) -> {issue_result(), state()}.
 issue_request(State, Domains) ->
-    case read_account_key() of
-       {ok, AccKey} ->
-           Config = ejabberd_option:acme(),
-           DirURL = maps:get(ca_url, Config, default_directory_url()),
-           Contact = maps:get(contact, Config, []),
-           CertType = maps:get(cert_type, Config, rsa),
-           issue_request(State, DirURL, Domains, AccKey, CertType, Contact);
-       {error, Reason} = Err ->
-           ?ERROR_MSG("Failed to request certificate for ~s: ~s",
-                      [misc:format_hosts_list(Domains),
-                       format_error(Reason)]),
+    case check_idna(Domains) of
+       ok ->
+           case read_account_key() of
+               {ok, AccKey} ->
+                   Config = ejabberd_option:acme(),
+                   DirURL = maps:get(ca_url, Config, default_directory_url()),
+                   Contact = maps:get(contact, Config, []),
+                   CertType = maps:get(cert_type, Config, rsa),
+                   issue_request(State, DirURL, Domains, AccKey, CertType, Contact);
+               {error, Reason} = Err ->
+                   ?ERROR_MSG("Failed to request certificate for ~s: ~s",
+                              [misc:format_hosts_list(Domains),
+                               format_error(Reason)]),
+                   {Err, State}
+           end;
+       {error, _} = Err ->
            {Err, State}
     end.
 
@@ -620,6 +627,16 @@ have_acme_listener() ->
              false
       end, ejabberd_option:listen()).
 
+-spec check_idna([binary()]) -> ok | {error, {idna_failed, binary()}}.
+check_idna([Domain|Domains]) ->
+    try idna:to_ascii(binary_to_list(Domain)) of
+       _ -> check_idna(Domains)
+    catch _:_ ->
+           {error, {idna_failed, Domain}}
+    end;
+check_idna([]) ->
+    ok.
+
 -spec format_error(term()) -> string().
 format_error({file, Reason}) ->
     "I/O error: " ++ file:format_error(Reason);
@@ -631,6 +648,8 @@ format_error(invalid_argument) ->
     "Invalid argument";
 format_error(unexpected_certfile) ->
     "The certificate file was not obtained using ACME";
+format_error({idna_failed, Domain}) ->
+    "Not an IDN hostname: " ++ binary_to_list(Domain);
 format_error({bad_cert, _, _} = Reason) ->
     "Malformed certificate file: " ++ pkix:format_error(Reason);
 format_error(Reason) ->