1: -module(sasl_external_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -include_lib("common_test/include/ct.hrl").
    6: -include_lib("eunit/include/eunit.hrl").
    7: -include_lib("exml/include/exml.hrl").
    8: 
    9: -import(domain_helper, [domain/0]).
   10: 
   11: all() ->
   12:     [
   13:      {group, fast_tls},
   14:      {group, just_tls}].
   15: 
   16: groups() ->
   17:     [{standard_keep_auth, [{group, registered}, {group, not_registered}]},
   18:      {registered, [parallel], [cert_one_xmpp_addrs_no_identity]},
   19:      {not_registered, [parallel], [cert_one_xmpp_addrs_no_identity_not_registered]},
   20:      {standard, [parallel], standard_test_cases()},
   21:      {use_common_name, [parallel], use_common_name_test_cases()},
   22:      {allow_just_user_identity, [parallel], allow_just_user_identity_test_cases()},
   23:      {demo_verification_module, [parallel], demo_verification_module_test_cases()},
   24:      {self_signed_certs_allowed, [parallel], self_signed_certs_allowed_test_cases()},
   25:      {self_signed_certs_not_allowed, [parallel], self_signed_certs_not_allowed_test_cases()},
   26:      {ca_signed, [self_signed_certs_not_allowed_group() | base_groups()]},
   27:      {self_signed, [self_signed_certs_allowed_group() | base_groups()]},
   28:      {fast_tls, [{group, ca_signed}]},
   29:      {just_tls, all_groups()} ].
   30: 
   31: all_groups() ->
   32:     [{group, self_signed},
   33:      {group, ca_signed}].
   34: 
   35: self_signed_certs_allowed_group() ->
   36:     {group, self_signed_certs_allowed}.
   37: self_signed_certs_not_allowed_group() ->
   38:     {group, self_signed_certs_not_allowed}.
   39: 
   40: base_groups() ->
   41:     [{group, standard},
   42:      {group, standard_keep_auth},
   43:      {group, use_common_name},
   44:      {group, allow_just_user_identity},
   45:      {group, demo_verification_module}].
   46: 
   47: standard_test_cases() ->
   48:     [
   49:      cert_no_xmpp_addrs_fails,
   50:      cert_no_xmpp_addrs_no_identity,
   51:      cert_one_xmpp_addr_identity_correct,
   52:      cert_one_xmpp_addrs_no_identity,
   53:      cert_one_xmpp_addr_wrong_hostname,
   54:      cert_more_xmpp_addrs_identity_correct,
   55:      cert_more_xmpp_addrs_no_identity_fails,
   56:      cert_more_xmpp_addrs_wrong_identity_fails
   57:     ].
   58: 
   59: use_common_name_test_cases() ->
   60:     [
   61:      cert_with_cn_no_xmpp_addrs_identity_correct,
   62:      cert_with_cn_no_xmpp_addrs_wrong_identity_fails,
   63:      cert_with_cn_no_xmpp_addrs_no_identity
   64:     ].
   65: 
   66: allow_just_user_identity_test_cases() ->
   67:     [
   68:      cert_no_xmpp_addrs_just_use_identity
   69:     ].
   70: 
   71: demo_verification_module_test_cases()->
   72:     [cert_no_xmpp_addrs_just_use_identity,
   73:      cert_one_xmpp_addrs_no_identity,
   74:      cert_with_jid_cn_no_xmpp_addrs_no_identity,
   75:      cert_with_jid_cn_many_xmpp_addrs_no_identity,
   76:      cert_more_xmpp_addrs_no_identity_fails,
   77:      cert_no_xmpp_addrs_no_identity].
   78: 
   79: self_signed_certs_allowed_test_cases() ->
   80:     [self_signed_cert_is_allowed_with_tls,
   81:      self_signed_cert_is_allowed_with_ws,
   82:      self_signed_cert_is_allowed_with_bosh,
   83:      no_cert_fails_to_authenticate].
   84: 
   85: self_signed_certs_not_allowed_test_cases() ->
   86:     [self_signed_cert_fails_to_authenticate_with_tls,
   87:      self_signed_cert_fails_to_authenticate_with_ws,
   88:      self_signed_cert_fails_to_authenticate_with_bosh,
   89:      ca_signed_cert_is_allowed_with_ws,
   90:      ca_signed_cert_is_allowed_with_bosh,
   91:      no_cert_fails_to_authenticate].
   92: 
   93: init_per_suite(Config) ->
   94:     Config0 = escalus:init_per_suite(Config),
   95:     Config1 = ejabberd_node_utils:init(Config0),
   96:     ejabberd_node_utils:backup_config_file(Config1),
   97:     generate_certs(Config1).
   98: 
   99: end_per_suite(Config) ->
  100:     ejabberd_node_utils:restore_config_file(Config),
  101:     ejabberd_node_utils:restart_application(mongooseim),
  102:     escalus:end_per_suite(Config).
  103: 
  104: init_per_group(just_tls, Config) ->
  105:     [{tls_module, "just_tls"} | Config];
  106: init_per_group(fast_tls, Config) ->
  107:     [{tls_module, "fast_tls"} | Config];
  108: init_per_group(ca_signed, Config) ->
  109:     [{signed, ca},
  110:      {ssl_options, "\n  tls.disconnect_on_failure = false"},
  111:      {verify_mode, "\n  tls.verify_mode = \"peer\""} | Config];
  112: init_per_group(self_signed, Config) ->
  113:     [{signed, self},
  114:      {verify_mode, "\n  tls.verify_mode = \"selfsigned_peer\""} | Config];
  115: init_per_group(standard, Config) ->
  116:     modify_config_and_restart("\"standard\"", Config),
  117:     Config;
  118: init_per_group(standard_keep_auth, Config) ->
  119:     Config1 = [{auth_methods, []} | Config],
  120:     modify_config_and_restart("\"standard\"", Config1),
  121:     case mongoose_helper:supports_sasl_module(cyrsasl_external) of
  122:         false -> {skip, "SASL External not supported"};
  123:         true -> Config1
  124:     end;
  125: init_per_group(registered, Config) ->
  126:     escalus:create_users(Config, [{bob, generate_user_tcp(Config, username("bob", Config))}]);
  127: init_per_group(use_common_name, Config) ->
  128:     modify_config_and_restart("\"standard\", \"common_name\"", Config),
  129:     Config;
  130: init_per_group(allow_just_user_identity, Config) ->
  131:     modify_config_and_restart("\"standard\", \"auth_id\"", Config),
  132:     Config;
  133: init_per_group(demo_verification_module, Config) ->
  134:     modify_config_and_restart("\"cyrsasl_external_verification\"", Config),
  135:     Config;
  136: init_per_group(self_signed_certs_allowed, Config) ->
  137:     modify_config_and_restart("\"standard\"", Config),
  138:     Config;
  139: init_per_group(self_signed_certs_not_allowed, Config) ->
  140:     modify_config_and_restart("\"standard\"", Config),
  141:     Config;
  142: init_per_group(_, Config) ->
  143:     Config.
  144: 
  145: modify_config_and_restart(CyrsaslExternalConfig, Config) ->
  146:     TLSModule = escalus_config:get_config(tls_module, Config, "just_tls"),
  147:     VerifyMode = escalus_config:get_config(verify_mode, Config, ""),
  148:     SSLOpts = case TLSModule of
  149:                   "just_tls" -> escalus_config:get_config(ssl_options, Config, "") ++ VerifyMode;
  150:                   "fast_tls" -> ""
  151:               end,
  152:     AuthMethods = escalus_config:get_config(auth_methods, Config,
  153:                                             [{auth_method, "pki"}, {auth_method_opts, false}]),
  154:     CACertFile = filename:join([path_helper:repo_dir(Config),
  155:                                 "tools", "ssl", "ca-clients", "cacert.pem"]),
  156:     NewConfigValues = [{tls_config, "tls.module = \"" ++ TLSModule ++ "\"\n"
  157:                                     "  tls.certfile = \"priv/ssl/fake_server.pem\"\n"
  158:                                     "  tls.mode = \"starttls\"\n"
  159:                                     "  tls.verify_peer = true\n"
  160:                                     "  tls.cacertfile = \"" ++ CACertFile ++ "\""
  161:                                     ++ SSLOpts},
  162: 		       {https_config, "tls.certfile = \"priv/ssl/fake_cert.pem\"\n"
  163:                                       "  tls.keyfile = \"priv/ssl/fake_key.pem\"\n"
  164:                                       "  tls.password = \"\"\n"
  165:                                       "  tls.verify_peer = true\n"
  166:                                       "  tls.cacertfile = \"" ++ CACertFile ++ "\""
  167:                                       ++ VerifyMode},
  168:                        {cyrsasl_external, CyrsaslExternalConfig},
  169: 		       {sasl_mechanisms, "\"external\""} | AuthMethods],
  170:     ejabberd_node_utils:modify_config_file(NewConfigValues, Config),
  171:     ejabberd_node_utils:restart_application(mongooseim).
  172: 
  173: end_per_group(registered, Config) ->
  174:     escalus:delete_users(Config, [{bob, generate_user_tcp(Config, username("bob", Config))}]);
  175: end_per_group(_, _Config) ->
  176:     ok.
  177: 
  178: cert_more_xmpp_addrs_identity_correct(C) ->
  179:     User = username("kate", C),
  180:     %% More than one xmpp_addr and specified identity, common_name not used
  181:     UserSpec = [{requested_name, requested_name(User)} |
  182: 		generate_user_tcp(C, User)],
  183:     {ok, Client, _} = escalus_connection:start(UserSpec),
  184:     escalus_connection:stop(Client).
  185: 
  186: cert_one_xmpp_addr_identity_correct(C) ->
  187:     User = username("bob", C),
  188:     UserSpec = [{requested_name, requested_name(User)} |
  189:                 generate_user_tcp(C, User)],
  190:     cert_fails_to_authenticate(UserSpec).
  191: 
  192: cert_no_xmpp_addrs_fails(C) ->
  193:     User = username("john", C),
  194:     UserSpec = [{requested_name, requested_name(User)} |
  195:                 generate_user_tcp(C, User)],
  196:     cert_fails_to_authenticate(UserSpec).
  197: 
  198: cert_no_xmpp_addrs_just_use_identity(C) ->
  199:     User = username("not-mike", C),
  200:     UserSpec = [{requested_name, requested_name("mike")} |
  201: 		generate_user_tcp(C, User)],
  202:     {ok, Client, _} = escalus_connection:start(UserSpec),
  203:     escalus_connection:stop(Client).
  204: 
  205: cert_no_xmpp_addrs_no_identity(C) ->
  206:     User = username("john", C),
  207:     UserSpec = generate_user_tcp(C, User),
  208:     cert_fails_to_authenticate(UserSpec).
  209: 
  210: cert_more_xmpp_addrs_no_identity_fails(C) ->
  211:     User = username("not-alice", C),
  212:     UserSpec = generate_user_tcp(C, User),
  213:     cert_fails_to_authenticate(UserSpec).
  214: 
  215: cert_one_xmpp_addrs_no_identity(C) ->
  216:     User = username("bob", C),
  217:     UserSpec = generate_user_tcp(C, User),
  218:     {ok, Client, _} = escalus_connection:start(UserSpec),
  219:     escalus_connection:stop(Client).
  220: 
  221: cert_one_xmpp_addrs_no_identity_not_registered(C) ->
  222:     User = username("bob", C),
  223:     UserSpec = generate_user_tcp(C, User),
  224:     cert_fails_to_authenticate(UserSpec).
  225: 
  226: cert_with_cn_no_xmpp_addrs_no_identity(C) ->
  227:     User = username("john", C),
  228:     UserSpec = generate_user_tcp(C, User),
  229:     {ok, Client, _} = escalus_connection:start(UserSpec),
  230:     escalus_connection:stop(Client).
  231: 
  232: cert_with_jid_cn_no_xmpp_addrs_no_identity(C) ->
  233:     User = add_domain_str("john"),
  234:     UserSpec = generate_user_tcp(C, User),
  235:     {ok, Client, _} = escalus_connection:start(UserSpec),
  236:     escalus_connection:stop(Client).
  237: 
  238: cert_with_jid_cn_many_xmpp_addrs_no_identity(C) ->
  239:     User = add_domain_str("grace"),
  240:     UserSpec = generate_user_tcp(C, User),
  241:     {ok, Client, _} = escalus_connection:start(UserSpec),
  242:     escalus_connection:stop(Client).
  243: 
  244: cert_with_cn_no_xmpp_addrs_identity_correct(C) ->
  245:     User = username("john", C),
  246:     UserSpec = [{requested_name, requested_name(User)} |
  247:                 generate_user_tcp(C, User)],
  248:     {ok, Client, _} = escalus_connection:start(UserSpec),
  249:     escalus_connection:stop(Client).
  250: 
  251: cert_with_cn_no_xmpp_addrs_wrong_identity_fails(C) ->
  252:     User = username("not-mike", C),
  253:     UserSpec = [{requested_name, requested_name("mike")} |
  254:                 generate_user_tcp(C, User)],
  255:     cert_fails_to_authenticate(UserSpec).
  256: 
  257: cert_more_xmpp_addrs_wrong_identity_fails(C) ->
  258:     User = username("grace", C),
  259:     UserSpec = [{requested_name, requested_name(User)} |
  260:                 generate_user_tcp(C, User)],
  261:     cert_fails_to_authenticate(UserSpec).
  262: 
  263: cert_one_xmpp_addr_wrong_hostname(C) ->
  264:     User = username("bob", C),
  265:     UserSpec = [{requested_name, requested_name(User)} |
  266:                 generate_user_tcp(C, User)],
  267:     cert_fails_to_authenticate(UserSpec).
  268: 
  269: ca_signed_cert_is_allowed_with_ws(C) ->
  270:     UserSpec = generate_user(C, "bob", escalus_ws),
  271:     {ok, Client, _} = escalus_connection:start(UserSpec),
  272: 
  273:     escalus_connection:stop(Client).
  274: 
  275: ca_signed_cert_is_allowed_with_bosh(C) ->
  276:     UserSpec = generate_user(C, "bob", escalus_bosh),
  277:     {ok, Client, _} = escalus_connection:start(UserSpec),
  278: 
  279:     escalus_connection:stop(Client).
  280: 
  281: self_signed_cert_fails_to_authenticate_with_tls(C) ->
  282:     self_signed_cert_fails_to_authenticate(C, escalus_tcp).
  283: 
  284: self_signed_cert_fails_to_authenticate_with_ws(C) ->
  285:     self_signed_cert_fails_to_authenticate(C, escalus_ws).
  286: 
  287: self_signed_cert_fails_to_authenticate_with_bosh(C) ->
  288:     self_signed_cert_fails_to_authenticate(C, escalus_bosh).
  289: 
  290: cert_fails_to_authenticate(UserSpec) ->
  291:     Self = self(),
  292:     F = fun() ->
  293: 		{ok, Client, _} = escalus_connection:start(UserSpec),
  294: 		Self ! escalus_connected,
  295: 		escalus_connection:stop(Client)
  296: 	end,
  297:     %% We spawn the process trying to connect because otherwise the testcase may crash
  298:     %% due linked process crash (client's process are started with start_link)
  299:     Pid = spawn(F),
  300:     MRef = erlang:monitor(process, Pid),
  301: 
  302:     receive
  303: 	{'DOWN', MRef, process, Pid, _Reason} ->
  304: 	    ok;
  305: 	escalus_connected ->
  306: 	    ct:fail(authenticated_but_should_not)
  307:     after 10000 ->
  308: 	      ct:fail(timeout_waiting_for_authentication_error)
  309:     end.
  310: 
  311: self_signed_cert_fails_to_authenticate(C, EscalusTransport) ->
  312:     Self = self(),
  313:     F = fun() ->
  314: 		UserSpec = generate_user(C, "greg-self-signed", EscalusTransport),
  315: 		{ok, Client, _} = escalus_connection:start(UserSpec),
  316: 		Self ! escalus_connected,
  317: 		escalus_connection:stop(Client)
  318: 	end,
  319:     %% We spawn the process trying to connect because otherwise the testcase may crash
  320:     %% due linked process crash (client's process are started with start_link)
  321:     Pid = spawn(F),
  322:     MRef = erlang:monitor(process, Pid),
  323: 
  324:     receive
  325: 	{'DOWN', MRef, process, Pid, _Reason} ->
  326: 	    ok;
  327: 	escalus_connected ->
  328: 	    ct:fail(authenticated_but_should_not)
  329:     after 10000 ->
  330: 	      ct:fail(timeout_waiting_for_authentication_error)
  331:     end.
  332: 
  333: self_signed_cert_is_allowed_with_tls(C) ->
  334:     self_signed_cert_is_allowed_with(escalus_tcp, C).
  335: 
  336: self_signed_cert_is_allowed_with_ws(C) ->
  337:     self_signed_cert_is_allowed_with(escalus_ws, C).
  338: 
  339: self_signed_cert_is_allowed_with_bosh(C) ->
  340:     self_signed_cert_is_allowed_with(escalus_bosh, C).
  341: 
  342: self_signed_cert_is_allowed_with(EscalusTransport, C) ->
  343:     UserSpec = generate_user(C, "bob-self-signed", EscalusTransport),
  344:     {ok, Client, _} = escalus_connection:start(UserSpec),
  345:     escalus_connection:stop(Client).
  346: 
  347: no_cert_fails_to_authenticate(_C) ->
  348:     UserSpec = [{username, <<"no_cert_user">>},
  349:                 {server, domain()},
  350:                 {host, <<"localhost">>},
  351:                 {port, ct:get_config({hosts, mim, c2s_port})},
  352:                 {password, <<"break_me">>},
  353:                 {resource, <<>>}, %% Allow the server to generate the resource
  354:                 {auth, {escalus_auth, auth_sasl_external}},
  355:                 {starttls, required}],
  356: 
  357:     Result = escalus_connection:start(UserSpec),
  358:     ?assertMatch({error, {connection_step_failed, _, _}}, Result),
  359:     {error, {connection_step_failed, _Call, Details}} = Result,
  360:     ?assertMatch({auth_failed, _, #xmlel{name = <<"failure">>}}, Details),
  361:     ok.
  362: 
  363: generate_certs(C) ->
  364:     CA = [#{cn => "not-alice", xmpp_addrs => [add_domain_str("alice"), "alice@fed1"]},
  365:           #{cn => "kate", xmpp_addrs => [add_domain_str("kate"), "kate@fed1"]},
  366:           #{cn => "bob", xmpp_addrs => [add_domain_str("bob")]},
  367:           #{cn => "greg", xmpp_addrs => [add_domain_str("greg")]},
  368:           #{cn => "john", xmpp_addrs => undefined},
  369:           #{cn => add_domain_str("john"), xmpp_addrs => undefined},
  370:           #{cn => "not-mike", xmpp_addrs => undefined},
  371:           #{cn => "grace", xmpp_addrs => ["grace@fed1", "grace@reg1"]},
  372:           #{cn => add_domain_str("grace"), xmpp_addrs => ["grace@fed1", "grace@reg1"]}],
  373:     SelfSigned = [ M#{cn => CN ++ "-self-signed", signed => self, xmpp_addrs => replace_addrs(Addrs)}
  374:                    || M = #{ cn := CN , xmpp_addrs := Addrs} <- CA],
  375:     CertSpecs = CA ++ SelfSigned,
  376:     Certs = [{maps:get(cn, CertSpec), generate_cert(C, CertSpec)} || CertSpec <- CertSpecs],
  377:     [{certs, maps:from_list(Certs)} | C].
  378: 
  379: generate_cert(C, #{cn := User} = CertSpec) ->
  380:     ConfigTemplate = filename:join(?config(mim_data_dir, C), "openssl-user.cnf"),
  381:     {ok, Template} = file:read_file(ConfigTemplate),
  382:     XMPPAddrs = maps:get(xmpp_addrs, CertSpec, undefined),
  383:     TemplateValues = prepare_template_values(User, XMPPAddrs),
  384:     OpenSSLConfig = bbmustache:render(Template, TemplateValues),
  385:     UserConfig = filename:join(?config(priv_dir, C), User ++ ".cfg"),
  386:     ct:log("OpenSSL config: ~ts~n~ts", [UserConfig, OpenSSLConfig]),
  387:     file:write_file(UserConfig, OpenSSLConfig),
  388:     UserKey = filename:join(?config(priv_dir, C), User ++ "_key.pem"),
  389: 
  390:     case maps:get(signed, CertSpec, ca) of
  391: 	ca ->
  392: 	    generate_ca_signed_cert(C, User, UserConfig, UserKey);
  393: 	self ->
  394: 	    generate_self_signed_cert(C, User, UserConfig, UserKey)
  395:     end.
  396: 
  397: generate_ca_signed_cert(C, User, UserConfig, UserKey ) ->
  398:     UserCsr = filename:join(?config(priv_dir, C), User ++ ".csr"),
  399:     Cmd = ["openssl req -config ", UserConfig, " -newkey rsa:2048 -sha256 -nodes -out ",
  400:            UserCsr, " -keyout ", UserKey, " -outform PEM"],
  401:     Out = os:cmd(Cmd),
  402:     ct:log("generate_ca_signed_cert 1:~nCmd ~p~nOut ~ts", [Cmd, Out]),
  403:     UserCert = filename:join(?config(priv_dir, C), User ++ "_cert.pem"),
  404:     SignCmd = filename:join(?config(mim_data_dir, C), "sign_cert.sh"),
  405:     Cmd2 = [SignCmd, " --req ", UserCsr, " --out ", UserCert],
  406:     SSLDir = filename:join([path_helper:repo_dir(C), "tools", "ssl"]),
  407:     OutLog = os:cmd("cd " ++ SSLDir ++ " && " ++ Cmd2),
  408:     ct:log("generate_ca_signed_cert 2:~nCmd ~p~nOut ~ts", [Cmd2, OutLog]),
  409:     #{key => UserKey,
  410:       cert => UserCert}.
  411: 
  412: generate_self_signed_cert(C, User, UserConfig, UserKey) ->
  413:     UserCert = filename:join(?config(priv_dir, C), User ++ "_self_signed_cert.pem"),
  414:     Cmd = ["openssl req -config ", UserConfig, " -newkey rsa:2048 -sha256 -nodes -out ",
  415:            UserCert, " -keyout ", UserKey, " -x509 -outform PEM -extensions client_req_extensions"],
  416:     OutLog = os:cmd(Cmd),
  417:     ct:log("generate_self_signed_cert:~nCmd ~p~nOut ~ts", [Cmd, OutLog]),
  418:     #{key => UserKey,
  419:       cert => UserCert}.
  420: 
  421: generate_user_tcp(C, User) ->
  422:     generate_user(C, User, escalus_tcp).
  423: 
  424: generate_user(C, User, Transport) ->
  425:     Certs = ?config(certs, C),
  426:     UserCert = maps:get(User, Certs),
  427: 
  428:     Common = [{username, list_to_binary(User)},
  429:               {server, domain()},
  430:               {host, <<"localhost">>},
  431:               {password, <<"break_me">>},
  432:               {resource, <<>>}, %% Allow the server to generate the resource
  433:               {auth, {escalus_auth, auth_sasl_external}},
  434:               {transport, Transport},
  435:               {ssl_opts, [{certfile, maps:get(cert, UserCert)},
  436:                           {keyfile, maps:get(key, UserCert)}]}],
  437:     Common ++ transport_specific_options(Transport)
  438:     ++ [{port, ct:get_config({hosts, mim, c2s_port})}].
  439: 
  440: transport_specific_options(escalus_tcp) ->
  441:     [{starttls, required}];
  442: transport_specific_options(_) ->
  443:      [{port, ct:get_config({hosts, mim, cowboy_secure_port})},
  444:       {ssl, true}].
  445: 
  446: prepare_template_values(User, XMPPAddrsIn) ->
  447:     Defaults = #{"cn" => User,
  448: 		 "xmppAddrs" => ""},
  449:     XMPPAddrs = maybe_prepare_xmpp_addresses(XMPPAddrsIn),
  450:     Defaults#{"xmppAddrs" => XMPPAddrs}.
  451: 
  452: maybe_prepare_xmpp_addresses(undefined) ->
  453:     "";
  454: maybe_prepare_xmpp_addresses(Addrs) when is_list(Addrs) ->
  455:     AddrsWithSeq = lists:zip(Addrs, lists:seq(1, length(Addrs))),
  456:     Entries = [make_xmpp_addr_entry(Addr, I) || {Addr, I} <- AddrsWithSeq],
  457:     string:join(Entries, "\n").
  458: 
  459: make_xmpp_addr_entry(Addr, I) ->
  460:     % id-on-xmppAddr OID is specified in the openssl-user.cnf config file
  461:     "otherName." ++ integer_to_list(I) ++ " = id-on-xmppAddr;UTF8:" ++ Addr.
  462: 
  463: requested_name(User) ->
  464:     add_domain(list_to_binary(User)).
  465: 
  466: username(Name, Config) ->
  467:     case escalus_config:get_config(signed, Config, ca) of
  468:         self ->
  469:             Name ++ "-self-signed";
  470:         ca ->
  471:             Name
  472:     end.
  473: 
  474: replace_addrs(undefined) ->
  475:     undefined;
  476: replace_addrs(Addresses) ->
  477:     lists:map( fun(Addr) -> [User, Hostname] = binary:split(list_to_binary(Addr), <<"@">>),
  478:                             binary_to_list(<<User/binary, <<"-self-signed@">>/binary, Hostname/binary>>) end, Addresses).
  479: 
  480: -spec add_domain_str(User :: string()) -> string().
  481: add_domain_str(User) ->
  482:     User ++ "@" ++ binary:bin_to_list(domain()).
  483: 
  484: -spec add_domain(User :: binary()) -> binary().
  485: add_domain(User) ->
  486:     <<User/binary, "@", (domain())/binary>>.