1 |
|
%%%============================================================================= |
2 |
|
%%% @copyright (C) 1999-2018, Erlang Solutions Ltd |
3 |
|
%%% @author Denys Gonchar <denys.gonchar@erlang-solutions.com> |
4 |
|
%%% @doc a dedicated module to provide SSL certificate parsing functionality. |
5 |
|
%%% @end |
6 |
|
%%%============================================================================= |
7 |
|
-module(cert_utils). |
8 |
|
-copyright("2018, Erlang Solutions Ltd."). |
9 |
|
-author('denys.gonchar@erlang-solutions.com'). |
10 |
|
|
11 |
|
-include("mongoose_logger.hrl"). |
12 |
|
|
13 |
|
-export([get_cert_domains/1, |
14 |
|
get_common_name/1, |
15 |
|
get_xmpp_addresses/1 |
16 |
|
]). |
17 |
|
|
18 |
|
-include_lib("public_key/include/public_key.hrl"). |
19 |
|
-include("XmppAddr.hrl"). |
20 |
|
-include("jlib.hrl"). |
21 |
|
|
22 |
|
-type certificate() :: #'Certificate'{}. |
23 |
|
|
24 |
|
-spec get_common_name(certificate()) -> bitstring() | error. |
25 |
|
get_common_name(Cert) -> |
26 |
70 |
try |
27 |
70 |
{rdnSequence, RDNSequence} = Cert#'Certificate'.tbsCertificate#'TBSCertificate'.subject, |
28 |
70 |
[{ok, {_, CN}}] = ['OTP-PUB-KEY':decode('X520CommonName', V) || |
29 |
70 |
AtributesList <- RDNSequence, |
30 |
79 |
#'AttributeTypeAndValue'{type = ?'id-at-commonName', value = V} <- AtributesList], |
31 |
70 |
CN |
32 |
|
catch |
33 |
|
Class:Exception:StackTrace -> |
34 |
:-( |
log_exception(Cert, Class, Exception, StackTrace), |
35 |
:-( |
error |
36 |
|
end. |
37 |
|
|
38 |
|
|
39 |
|
-spec get_xmpp_addresses(certificate()) -> [bitstring()]. |
40 |
|
get_xmpp_addresses(Cert) -> |
41 |
70 |
try |
42 |
70 |
Extensions = Cert#'Certificate'.tbsCertificate#'TBSCertificate'.extensions, |
43 |
70 |
[BinVal] = [convert_to_bin(V) || |
44 |
70 |
#'Extension'{extnID = ?'id-ce-subjectAltName', extnValue = V} <- Extensions], |
45 |
70 |
{ok, SANs} = 'OTP-PUB-KEY':decode('SubjectAltName', BinVal), |
46 |
70 |
XmppAddresses = |
47 |
|
[begin |
48 |
55 |
case 'XmppAddr':decode('XmppAddr', V) of |
49 |
55 |
{ok, XmppAddr} -> XmppAddr; |
50 |
|
Error -> |
51 |
:-( |
?LOG_DEBUG(#{what => get_xmpp_addresses_failed, |
52 |
|
text => <<"'XmppAddr':decode/2 failed">>, |
53 |
:-( |
cert => Cert, reason => Error}), |
54 |
:-( |
ok |
55 |
|
end |
56 |
70 |
end || {otherName, #'AnotherName'{'type-id' = ?'id-on-xmppAddr', value = V}} <- SANs], |
57 |
70 |
[Addr || Addr <- XmppAddresses, is_binary(Addr)] |
58 |
|
catch |
59 |
|
Class:Exception:StackTrace -> |
60 |
:-( |
log_exception(Cert, Class, Exception,StackTrace), |
61 |
:-( |
[] |
62 |
|
end. |
63 |
|
|
64 |
|
|
65 |
|
-spec get_dns_addresses(certificate()) -> [string()]. |
66 |
|
get_dns_addresses(Cert) -> |
67 |
3 |
try |
68 |
3 |
Extensions = Cert#'Certificate'.tbsCertificate#'TBSCertificate'.extensions, |
69 |
3 |
[BinVal] = [convert_to_bin(V) || |
70 |
3 |
#'Extension'{extnID = ?'id-ce-subjectAltName', extnValue = V} <- Extensions], |
71 |
3 |
{ok, SANs} = 'OTP-PUB-KEY':decode('SubjectAltName', BinVal), |
72 |
3 |
[DNS || {dNSName, DNS} <- SANs] |
73 |
|
catch |
74 |
|
Class:Exception:StackTrace -> |
75 |
:-( |
log_exception(Cert, Class, Exception, StackTrace), |
76 |
:-( |
[] |
77 |
|
end. |
78 |
|
|
79 |
|
|
80 |
|
-spec get_cert_domains(certificate()) -> [bitstring()]. |
81 |
|
get_cert_domains(Cert) -> |
82 |
3 |
CN = get_common_name(Cert), |
83 |
3 |
Addresses = get_xmpp_addresses(Cert), |
84 |
3 |
Domains = get_dns_addresses(Cert), |
85 |
3 |
lists:append([get_lserver_from_addr(CN, false) | |
86 |
:-( |
[get_lserver_from_addr(Addr, true) || Addr <- Addresses, is_binary(Addr)] ++ |
87 |
18 |
[get_lserver_from_addr(DNS, false) || DNS <- Domains, is_list(DNS)]]). |
88 |
|
|
89 |
|
|
90 |
|
convert_to_bin(Val) when is_list(Val) -> |
91 |
18 |
list_to_binary(Val); |
92 |
|
convert_to_bin(Val) -> |
93 |
76 |
Val. |
94 |
|
|
95 |
|
-spec get_lserver_from_addr(bitstring() | string(), boolean()) -> [bitstring()]. |
96 |
|
get_lserver_from_addr(V, UTF8) when is_binary(V); is_list(V) -> |
97 |
21 |
Val = convert_to_bin(V), |
98 |
21 |
case {jid:from_binary(Val), UTF8} of |
99 |
|
{#jid{luser = <<"">>, lserver = LD, lresource = <<"">>}, true} -> |
100 |
:-( |
case mongoose_s2s_lib:domain_utf8_to_ascii(LD) of |
101 |
:-( |
false -> []; |
102 |
:-( |
PCLD -> [PCLD] |
103 |
|
end; |
104 |
6 |
{#jid{luser = <<"">>, lserver = LD, lresource = <<"">>}, _} -> [LD]; |
105 |
15 |
_ -> [] |
106 |
|
end; |
107 |
:-( |
get_lserver_from_addr(_, _) -> []. |
108 |
|
|
109 |
|
|
110 |
|
log_exception(_Cert, Class, Exception, StackTrace) -> |
111 |
:-( |
?LOG_ERROR(#{what => <<"cert_parsing_failed">>, |
112 |
|
text => <<"failed to parse certificate">>, |
113 |
:-( |
class => Class, reason => Exception, stacktrace => StackTrace}). |