mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
* src/ejabberd_s2s_out.erl: Support for STARTTLS+SASL EXTERNAL
* src/ejabberd_s2s_in.erl: Likewise * src/tls/tls.erl: Likewise * src/tls/tls_drv.c: Likewise * src/tls/XmppAddr.asn1: Likewise * src/tls/Makefile.in: Likewise SVN Revision: 430
This commit is contained in:
parent
2efda30fdc
commit
f6343f01f7
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
|||||||
|
2005-11-03 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
|
* src/ejabberd_s2s_out.erl: Support for STARTTLS+SASL EXTERNAL
|
||||||
|
(not well-tested yet)
|
||||||
|
* src/ejabberd_s2s_in.erl: Likewise
|
||||||
|
* src/tls/tls.erl: Likewise
|
||||||
|
* src/tls/tls_drv.c: Likewise
|
||||||
|
* src/tls/XmppAddr.asn1: Likewise
|
||||||
|
* src/tls/Makefile.in: Likewise
|
||||||
|
|
||||||
2005-10-30 Alexey Shchepin <alexey@sevcom.net>
|
2005-10-30 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/mod_disco.erl: Minor fix
|
* src/mod_disco.erl: Minor fix
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
%-include_lib("ssl/pkix/SSL-PKIX.hrl").
|
||||||
|
-include_lib("ssl/pkix/PKIX1Explicit88.hrl").
|
||||||
|
-include("tls/XmppAddr.hrl").
|
||||||
|
|
||||||
-define(DICT, dict).
|
-define(DICT, dict).
|
||||||
|
|
||||||
@ -40,6 +43,8 @@
|
|||||||
tls = false,
|
tls = false,
|
||||||
tls_enabled = false,
|
tls_enabled = false,
|
||||||
tls_options = [],
|
tls_options = [],
|
||||||
|
authenticated = false,
|
||||||
|
auth_domain,
|
||||||
connections = ?DICT:new(),
|
connections = ?DICT:new(),
|
||||||
timer}).
|
timer}).
|
||||||
|
|
||||||
@ -131,18 +136,50 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
|
|
||||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||||
% TODO
|
|
||||||
case {xml:get_attr_s("xmlns", Attrs),
|
case {xml:get_attr_s("xmlns", Attrs),
|
||||||
xml:get_attr_s("xmlns:db", Attrs),
|
xml:get_attr_s("xmlns:db", Attrs),
|
||||||
xml:get_attr_s("version", Attrs) == "1.0"} of
|
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||||
{"jabber:server", "jabber:server:dialback", true} when
|
{"jabber:server", "jabber:server:dialback", true} when
|
||||||
StateData#state.tls ->
|
StateData#state.tls and (not StateData#state.authenticated) ->
|
||||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||||
|
SASL =
|
||||||
|
if
|
||||||
|
StateData#state.tls_enabled ->
|
||||||
|
case tls:get_peer_certificate(StateData#state.socket) of
|
||||||
|
{ok, _Cert} ->
|
||||||
|
case tls:get_verify_result(
|
||||||
|
StateData#state.socket) of
|
||||||
|
0 ->
|
||||||
|
[{xmlelement, "mechanisms",
|
||||||
|
[{"xmlns", ?NS_SASL}],
|
||||||
|
[{xmlelement, "mechanism", [],
|
||||||
|
[{xmlcdata, "EXTERNAL"}]}]}];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
error ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
StartTLS = if
|
||||||
|
StateData#state.tls_enabled ->
|
||||||
|
[];
|
||||||
|
true ->
|
||||||
|
[{xmlelement, "starttls",
|
||||||
|
[{"xmlns", ?NS_TLS}], []}]
|
||||||
|
end,
|
||||||
send_element(StateData,
|
send_element(StateData,
|
||||||
{xmlelement, "stream:features", [],
|
{xmlelement, "stream:features", [],
|
||||||
[{xmlelement, "starttls",
|
SASL ++ StartTLS}),
|
||||||
[{"xmlns", ?NS_TLS}], []}]}),
|
|
||||||
{next_state, wait_for_feature_request, StateData};
|
{next_state, wait_for_feature_request, StateData};
|
||||||
|
{"jabber:server", _, true} when
|
||||||
|
StateData#state.authenticated ->
|
||||||
|
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "stream:features", [], []}),
|
||||||
|
{next_state, stream_established, StateData};
|
||||||
{"jabber:server", "jabber:server:dialback", _} ->
|
{"jabber:server", "jabber:server:dialback", _} ->
|
||||||
send_text(StateData, ?STREAM_HEADER("")),
|
send_text(StateData, ?STREAM_HEADER("")),
|
||||||
{next_state, stream_established, StateData};
|
{next_state, stream_established, StateData};
|
||||||
@ -185,6 +222,60 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
|||||||
streamid = new_id(),
|
streamid = new_id(),
|
||||||
tls_enabled = true
|
tls_enabled = true
|
||||||
}};
|
}};
|
||||||
|
{?NS_SASL, "auth"} when TLSEnabled ->
|
||||||
|
Mech = xml:get_attr_s("mechanism", Attrs),
|
||||||
|
case Mech of
|
||||||
|
"EXTERNAL" ->
|
||||||
|
Auth = jlib:decode_base64(xml:get_cdata(Els)),
|
||||||
|
AuthDomain = jlib:nameprep(Auth),
|
||||||
|
AuthRes =
|
||||||
|
case tls:get_peer_certificate(StateData#state.socket) of
|
||||||
|
{ok, Cert} ->
|
||||||
|
case tls:get_verify_result(
|
||||||
|
StateData#state.socket) of
|
||||||
|
0 ->
|
||||||
|
case AuthDomain of
|
||||||
|
error ->
|
||||||
|
false;
|
||||||
|
_ ->
|
||||||
|
lists:member(
|
||||||
|
AuthDomain,
|
||||||
|
get_cert_domains(Cert))
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
error ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
if
|
||||||
|
AuthRes ->
|
||||||
|
ejabberd_receiver:reset_stream(
|
||||||
|
StateData#state.receiver),
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "success",
|
||||||
|
[{"xmlns", ?NS_SASL}], []}),
|
||||||
|
?INFO_MSG("(~w) Accepted s2s authentication for ~s",
|
||||||
|
[StateData#state.socket, AuthDomain]),
|
||||||
|
{next_state, wait_for_stream,
|
||||||
|
StateData#state{streamid = new_id(),
|
||||||
|
authenticated = true,
|
||||||
|
auth_domain = AuthDomain
|
||||||
|
}};
|
||||||
|
true ->
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "failure",
|
||||||
|
[{"xmlns", ?NS_SASL}], []}),
|
||||||
|
send_text(StateData, ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "failure",
|
||||||
|
[{"xmlns", ?NS_SASL}],
|
||||||
|
[{xmlelement, "invalid-mechanism", [], []}]}),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
stream_established({xmlstreamelement, El}, StateData)
|
stream_established({xmlstreamelement, El}, StateData)
|
||||||
end;
|
end;
|
||||||
@ -252,18 +343,38 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
|||||||
(To /= error) and (From /= error) ->
|
(To /= error) and (From /= error) ->
|
||||||
LFrom = From#jid.lserver,
|
LFrom = From#jid.lserver,
|
||||||
LTo = To#jid.lserver,
|
LTo = To#jid.lserver,
|
||||||
case ?DICT:find({LFrom, LTo},
|
if
|
||||||
StateData#state.connections) of
|
StateData#state.authenticated ->
|
||||||
{ok, established} ->
|
case (LFrom == StateData#state.auth_domain)
|
||||||
if ((Name == "iq") or
|
andalso
|
||||||
(Name == "message") or
|
lists:member(
|
||||||
(Name == "presence")) ->
|
LTo,
|
||||||
ejabberd_router:route(From, To, El);
|
ejabberd_router:dirty_get_all_domains()) of
|
||||||
true ->
|
true ->
|
||||||
|
if ((Name == "iq") or
|
||||||
|
(Name == "message") or
|
||||||
|
(Name == "presence")) ->
|
||||||
|
ejabberd_router:route(From, To, El);
|
||||||
|
true ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
error
|
error
|
||||||
end;
|
end;
|
||||||
_ ->
|
true ->
|
||||||
error
|
case ?DICT:find({LFrom, LTo},
|
||||||
|
StateData#state.connections) of
|
||||||
|
{ok, established} ->
|
||||||
|
if ((Name == "iq") or
|
||||||
|
(Name == "message") or
|
||||||
|
(Name == "presence")) ->
|
||||||
|
ejabberd_router:route(From, To, El);
|
||||||
|
true ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
error
|
error
|
||||||
@ -365,7 +476,7 @@ handle_info({send_text, Text}, StateName, StateData) ->
|
|||||||
send_text(StateData, Text),
|
send_text(StateData, Text),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
|
|
||||||
handle_info({timeout, Timer, _}, StateName,
|
handle_info({timeout, Timer, _}, _StateName,
|
||||||
#state{timer = Timer} = StateData) ->
|
#state{timer = Timer} = StateData) ->
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
@ -428,4 +539,51 @@ is_key_packet(_) ->
|
|||||||
false.
|
false.
|
||||||
|
|
||||||
|
|
||||||
|
get_cert_domains(Cert) ->
|
||||||
|
{rdnSequence, Subject} =
|
||||||
|
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject,
|
||||||
|
lists:flatmap(
|
||||||
|
fun(#'AttributeTypeAndValue'{type = ?'id-at-commonName',
|
||||||
|
value = Val}) ->
|
||||||
|
case 'PKIX1Explicit88':decode(
|
||||||
|
'X520CommonName', Val) of
|
||||||
|
{ok, {_, D1}} ->
|
||||||
|
D = if
|
||||||
|
is_list(D1) -> D1;
|
||||||
|
is_binary(D1) -> binary_to_list(D1);
|
||||||
|
true -> error
|
||||||
|
end,
|
||||||
|
if
|
||||||
|
D /= error ->
|
||||||
|
case jlib:nameprep(D) of
|
||||||
|
error ->
|
||||||
|
[];
|
||||||
|
LD ->
|
||||||
|
[LD]
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
(#'AttributeTypeAndValue'{type = ?'id-on-xmppAddr',
|
||||||
|
value = Val}) ->
|
||||||
|
case 'XmppAddr':decode(
|
||||||
|
'XmppAddr', Val) of
|
||||||
|
{ok, D} when is_binary(D) ->
|
||||||
|
case jlib:nameprep(binary_to_list(D)) of
|
||||||
|
error ->
|
||||||
|
[];
|
||||||
|
LD ->
|
||||||
|
[LD]
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
(_) ->
|
||||||
|
[]
|
||||||
|
end, lists:flatten(Subject)).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
wait_for_stream/2,
|
wait_for_stream/2,
|
||||||
wait_for_validation/2,
|
wait_for_validation/2,
|
||||||
wait_for_features/2,
|
wait_for_features/2,
|
||||||
|
wait_for_auth_result/2,
|
||||||
wait_for_starttls_proceed/2,
|
wait_for_starttls_proceed/2,
|
||||||
stream_established/2,
|
stream_established/2,
|
||||||
handle_event/3,
|
handle_event/3,
|
||||||
@ -40,6 +41,8 @@
|
|||||||
tls_required = false,
|
tls_required = false,
|
||||||
tls_enabled = false,
|
tls_enabled = false,
|
||||||
tls_options = [],
|
tls_options = [],
|
||||||
|
authenticated = false,
|
||||||
|
try_auth = true,
|
||||||
myname, server, queue,
|
myname, server, queue,
|
||||||
new = false, verify = false,
|
new = false, verify = false,
|
||||||
timer}).
|
timer}).
|
||||||
@ -276,23 +279,57 @@ wait_for_validation(closed, StateData) ->
|
|||||||
wait_for_features({xmlstreamelement, El}, StateData) ->
|
wait_for_features({xmlstreamelement, El}, StateData) ->
|
||||||
case El of
|
case El of
|
||||||
{xmlelement, "stream:features", _Attrs, Els} ->
|
{xmlelement, "stream:features", _Attrs, Els} ->
|
||||||
{StartTLS, StartTLSRequired} =
|
{SASLEXT, StartTLS, StartTLSRequired} =
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun({xmlelement, "starttls", Attrs1, Els1} = El1, Acc) ->
|
fun({xmlelement, "mechanisms", Attrs1, Els1} = El1,
|
||||||
|
{SEXT, STLS, STLSReq} = Acc) ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs1) of
|
||||||
|
?NS_SASL ->
|
||||||
|
NewSEXT =
|
||||||
|
lists:any(
|
||||||
|
fun({xmlelement, "mechanism", _, Els2}) ->
|
||||||
|
case xml:get_cdata(Els2) of
|
||||||
|
"EXTERNAL" -> true;
|
||||||
|
_ -> false
|
||||||
|
end;
|
||||||
|
(_) -> false
|
||||||
|
end, Els1),
|
||||||
|
{NewSEXT, STLS, STLSReq};
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end;
|
||||||
|
({xmlelement, "starttls", Attrs1, Els1} = El1,
|
||||||
|
{SEXT, STLS, STLSReq} = Acc) ->
|
||||||
case xml:get_attr_s("xmlns", Attrs1) of
|
case xml:get_attr_s("xmlns", Attrs1) of
|
||||||
?NS_TLS ->
|
?NS_TLS ->
|
||||||
Req = case xml:get_subtag(El1, "required") of
|
Req = case xml:get_subtag(El1, "required") of
|
||||||
{xmlelement, _, _, _} -> true;
|
{xmlelement, _, _, _} -> true;
|
||||||
false -> false
|
false -> false
|
||||||
end,
|
end,
|
||||||
{true, Req};
|
{SEXT, true, Req};
|
||||||
_ ->
|
_ ->
|
||||||
Acc
|
Acc
|
||||||
end;
|
end;
|
||||||
(_, Acc) ->
|
(_, Acc) ->
|
||||||
Acc
|
Acc
|
||||||
end, {false, false}, Els),
|
end, {false, false, false}, Els),
|
||||||
if
|
if
|
||||||
|
(not SASLEXT) and (not StartTLS) and
|
||||||
|
StateData#state.authenticated ->
|
||||||
|
send_queue(StateData, StateData#state.queue),
|
||||||
|
{next_state, stream_established,
|
||||||
|
StateData#state{queue = queue:new()}};
|
||||||
|
SASLEXT and StateData#state.try_auth and
|
||||||
|
(StateData#state.new /= false) ->
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "auth",
|
||||||
|
[{"xmlns", ?NS_SASL},
|
||||||
|
{"mechanism", "EXTERNAL"}],
|
||||||
|
[{xmlcdata,
|
||||||
|
jlib:encode_base64(
|
||||||
|
StateData#state.myname)}]}),
|
||||||
|
{next_state, wait_for_auth_result,
|
||||||
|
StateData#state{try_auth = false}};
|
||||||
StartTLS and StateData#state.tls and
|
StartTLS and StateData#state.tls and
|
||||||
(not StateData#state.tls_enabled) ->
|
(not StateData#state.tls_enabled) ->
|
||||||
StateData#state.receiver ! {change_timeout, 100},
|
StateData#state.receiver ! {change_timeout, 100},
|
||||||
@ -333,6 +370,66 @@ wait_for_features(closed, StateData) ->
|
|||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_auth_result({xmlstreamelement, El}, StateData) ->
|
||||||
|
case El of
|
||||||
|
{xmlelement, "success", Attrs, _Els} ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
|
?NS_SASL ->
|
||||||
|
?INFO_MSG("auth: ~p", [{StateData#state.myname,
|
||||||
|
StateData#state.server}]),
|
||||||
|
ejabberd_receiver:reset_stream(
|
||||||
|
StateData#state.receiver),
|
||||||
|
send_text(StateData,
|
||||||
|
io_lib:format(?STREAM_HEADER,
|
||||||
|
[StateData#state.server,
|
||||||
|
" version='1.0'"])),
|
||||||
|
{next_state, wait_for_stream,
|
||||||
|
StateData#state{streamid = new_id(),
|
||||||
|
authenticated = true
|
||||||
|
}};
|
||||||
|
_ ->
|
||||||
|
send_text(StateData,
|
||||||
|
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||||
|
?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
{xmlelement, "failure", Attrs, _Els} ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
|
?NS_SASL ->
|
||||||
|
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||||
|
StateData#state.server}]),
|
||||||
|
(StateData#state.sockmod):close(StateData#state.socket),
|
||||||
|
gen_fsm:send_event(self(), init),
|
||||||
|
{next_state, open_socket,
|
||||||
|
StateData#state{socket = undefined}};
|
||||||
|
_ ->
|
||||||
|
send_text(StateData,
|
||||||
|
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||||
|
?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
send_text(StateData,
|
||||||
|
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||||
|
?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_auth_result({xmlstreamend, Name}, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_auth_result({xmlstreamerror, _}, StateData) ->
|
||||||
|
send_text(StateData,
|
||||||
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_auth_result(timeout, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_auth_result(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
||||||
case El of
|
case El of
|
||||||
{xmlelement, "proceed", Attrs, _Els} ->
|
{xmlelement, "proceed", Attrs, _Els} ->
|
||||||
@ -351,12 +448,15 @@ wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
|||||||
streamid = new_id(),
|
streamid = new_id(),
|
||||||
tls_enabled = true
|
tls_enabled = true
|
||||||
},
|
},
|
||||||
R = send_text(NewStateData,
|
send_text(NewStateData,
|
||||||
io_lib:format(?STREAM_HEADER,
|
io_lib:format(?STREAM_HEADER,
|
||||||
[StateData#state.server,
|
[StateData#state.server,
|
||||||
" version='1.0'"])),
|
" version='1.0'"])),
|
||||||
{next_state, wait_for_stream, NewStateData};
|
{next_state, wait_for_stream, NewStateData};
|
||||||
_ ->
|
_ ->
|
||||||
|
send_text(StateData,
|
||||||
|
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||||
|
?STREAM_TRAILER),
|
||||||
{stop, normal, StateData}
|
{stop, normal, StateData}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -12,14 +12,18 @@ ERLSHLIBS = ../tls_drv.so
|
|||||||
|
|
||||||
OUTDIR = ..
|
OUTDIR = ..
|
||||||
EFLAGS = -I .. -pz ..
|
EFLAGS = -I .. -pz ..
|
||||||
|
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
|
||||||
OBJS = \
|
OBJS = \
|
||||||
$(OUTDIR)/tls.beam
|
$(OUTDIR)/tls.beam $(OUTDIR)/XmppAddr.beam
|
||||||
|
|
||||||
all: $(OBJS) $(ERLSHLIBS)
|
all: $(OBJS) $(ERLSHLIBS)
|
||||||
|
|
||||||
$(OUTDIR)/%.beam: %.erl
|
$(OUTDIR)/%.beam: %.erl
|
||||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||||
|
|
||||||
|
%.erl: %.asn1
|
||||||
|
erlc $(ASN_FLAGS) $<
|
||||||
|
|
||||||
#all: $(ERLSHLIBS)
|
#all: $(ERLSHLIBS)
|
||||||
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||||
|
|
||||||
|
14
src/tls/XmppAddr.asn1
Normal file
14
src/tls/XmppAddr.asn1
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
XmppAddr { iso(1) identified-organization(3)
|
||||||
|
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||||
|
id-on(8) id-on-xmppAddr(5) }
|
||||||
|
|
||||||
|
DEFINITIONS EXPLICIT TAGS ::=
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
||||||
|
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||||
|
id-on(8) 5 }
|
||||||
|
|
||||||
|
XmppAddr ::= UTF8String
|
||||||
|
|
||||||
|
END
|
@ -17,6 +17,8 @@
|
|||||||
send/2,
|
send/2,
|
||||||
recv/2, recv/3, recv_data/2,
|
recv/2, recv/3, recv_data/2,
|
||||||
close/1,
|
close/1,
|
||||||
|
get_peer_certificate/1,
|
||||||
|
get_verify_result/1,
|
||||||
test/0]).
|
test/0]).
|
||||||
|
|
||||||
%% Internal exports, call-back functions.
|
%% Internal exports, call-back functions.
|
||||||
@ -33,6 +35,8 @@
|
|||||||
-define(SET_DECRYPTED_OUTPUT, 4).
|
-define(SET_DECRYPTED_OUTPUT, 4).
|
||||||
-define(GET_ENCRYPTED_OUTPUT, 5).
|
-define(GET_ENCRYPTED_OUTPUT, 5).
|
||||||
-define(GET_DECRYPTED_INPUT, 6).
|
-define(GET_DECRYPTED_INPUT, 6).
|
||||||
|
-define(GET_PEER_CERTIFICATE, 7).
|
||||||
|
-define(GET_VERIFY_RESULT, 8).
|
||||||
|
|
||||||
-record(tlssock, {tcpsock, tlsport}).
|
-record(tlssock, {tcpsock, tlsport}).
|
||||||
|
|
||||||
@ -69,15 +73,16 @@ handle_call(_, _, State) ->
|
|||||||
handle_cast(_, State) ->
|
handle_cast(_, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
handle_info({'EXIT', Pid, Reason}, Port) ->
|
|
||||||
{noreply, Port};
|
|
||||||
|
|
||||||
handle_info({'EXIT', Port, Reason}, Port) ->
|
handle_info({'EXIT', Port, Reason}, Port) ->
|
||||||
{stop, {port_died, Reason}, Port};
|
{stop, {port_died, Reason}, Port};
|
||||||
|
|
||||||
|
handle_info({'EXIT', _Pid, _Reason}, Port) ->
|
||||||
|
{noreply, Port};
|
||||||
|
|
||||||
handle_info(_, State) ->
|
handle_info(_, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
code_change(OldVsn, State, Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
terminate(_Reason, Port) ->
|
terminate(_Reason, Port) ->
|
||||||
@ -115,7 +120,7 @@ tls_to_tcp(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
|
|||||||
|
|
||||||
recv(Socket, Length) ->
|
recv(Socket, Length) ->
|
||||||
recv(Socket, Length, infinity).
|
recv(Socket, Length, infinity).
|
||||||
recv(#tlssock{tcpsock = TCPSocket, tlsport = Port} = TLSSock,
|
recv(#tlssock{tcpsock = TCPSocket} = TLSSock,
|
||||||
Length, Timeout) ->
|
Length, Timeout) ->
|
||||||
case gen_tcp:recv(TCPSocket, Length, Timeout) of
|
case gen_tcp:recv(TCPSocket, Length, Timeout) of
|
||||||
{ok, Packet} ->
|
{ok, Packet} ->
|
||||||
@ -133,6 +138,7 @@ recv_data(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
|||||||
<<0, Out/binary>> ->
|
<<0, Out/binary>> ->
|
||||||
case gen_tcp:send(TCPSocket, Out) of
|
case gen_tcp:send(TCPSocket, Out) of
|
||||||
ok ->
|
ok ->
|
||||||
|
%io:format("IN: ~p~n", [{TCPSocket, binary_to_list(In)}]),
|
||||||
{ok, In};
|
{ok, In};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
@ -150,6 +156,7 @@ recv_data(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
|||||||
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
||||||
case port_control(Port, ?SET_DECRYPTED_OUTPUT, Packet) of
|
case port_control(Port, ?SET_DECRYPTED_OUTPUT, Packet) of
|
||||||
<<0>> ->
|
<<0>> ->
|
||||||
|
%io:format("OUT: ~p~n", [{TCPSocket, lists:flatten(Packet)}]),
|
||||||
case port_control(Port, ?GET_ENCRYPTED_OUTPUT, []) of
|
case port_control(Port, ?GET_ENCRYPTED_OUTPUT, []) of
|
||||||
<<0, Out/binary>> ->
|
<<0, Out/binary>> ->
|
||||||
gen_tcp:send(TCPSocket, Out);
|
gen_tcp:send(TCPSocket, Out);
|
||||||
@ -159,8 +166,12 @@ send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
|||||||
<<1, Error/binary>> ->
|
<<1, Error/binary>> ->
|
||||||
{error, binary_to_list(Error)};
|
{error, binary_to_list(Error)};
|
||||||
<<2>> -> % Dirty hack
|
<<2>> -> % Dirty hack
|
||||||
receive after 100 -> ok end,
|
receive
|
||||||
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet)
|
{timeout, _Timer, _} ->
|
||||||
|
{error, timeout}
|
||||||
|
after 100 ->
|
||||||
|
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet)
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@ -168,6 +179,23 @@ close(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
|
|||||||
gen_tcp:close(TCPSocket),
|
gen_tcp:close(TCPSocket),
|
||||||
port_close(Port).
|
port_close(Port).
|
||||||
|
|
||||||
|
get_peer_certificate(#tlssock{tlsport = Port}) ->
|
||||||
|
case port_control(Port, ?GET_PEER_CERTIFICATE, []) of
|
||||||
|
<<0, BCert/binary>> ->
|
||||||
|
case catch ssl_pkix:decode_cert(BCert, [pkix]) of
|
||||||
|
{ok, Cert} ->
|
||||||
|
{ok, Cert};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
<<1>> ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_verify_result(#tlssock{tlsport = Port}) ->
|
||||||
|
<<Res>> = port_control(Port, ?GET_VERIFY_RESULT, []),
|
||||||
|
Res.
|
||||||
|
|
||||||
|
|
||||||
test() ->
|
test() ->
|
||||||
case erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv) of
|
case erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv) of
|
||||||
|
@ -46,12 +46,19 @@ static void tls_drv_stop(ErlDrvData handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#define SET_CERTIFICATE_FILE_ACCEPT 1
|
#define SET_CERTIFICATE_FILE_ACCEPT 1
|
||||||
#define SET_CERTIFICATE_FILE_CONNECT 2
|
#define SET_CERTIFICATE_FILE_CONNECT 2
|
||||||
#define SET_ENCRYPTED_INPUT 3
|
#define SET_ENCRYPTED_INPUT 3
|
||||||
#define SET_DECRYPTED_OUTPUT 4
|
#define SET_DECRYPTED_OUTPUT 4
|
||||||
#define GET_ENCRYPTED_OUTPUT 5
|
#define GET_ENCRYPTED_OUTPUT 5
|
||||||
#define GET_DECRYPTED_INPUT 6
|
#define GET_DECRYPTED_INPUT 6
|
||||||
|
#define GET_PEER_CERTIFICATE 7
|
||||||
|
#define GET_VERIFY_RESULT 8
|
||||||
|
|
||||||
|
|
||||||
#define die_unless(cond, errstr) \
|
#define die_unless(cond, errstr) \
|
||||||
@ -75,6 +82,7 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
int res;
|
int res;
|
||||||
int size;
|
int size;
|
||||||
ErlDrvBinary *b;
|
ErlDrvBinary *b;
|
||||||
|
X509 *cert;
|
||||||
|
|
||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
@ -92,6 +100,15 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
res = SSL_CTX_check_private_key(d->ctx);
|
res = SSL_CTX_check_private_key(d->ctx);
|
||||||
die_unless(res > 0, "SSL_CTX_check_private_key failed");
|
die_unless(res > 0, "SSL_CTX_check_private_key failed");
|
||||||
|
|
||||||
|
SSL_CTX_set_default_verify_paths(d->ctx);
|
||||||
|
|
||||||
|
if (command == SET_CERTIFICATE_FILE_ACCEPT)
|
||||||
|
{
|
||||||
|
SSL_CTX_set_verify(d->ctx,
|
||||||
|
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
|
||||||
|
verify_callback);
|
||||||
|
}
|
||||||
|
|
||||||
d->ssl = SSL_new(d->ctx);
|
d->ssl = SSL_new(d->ctx);
|
||||||
die_unless(d->ssl, "SSL_new failed");
|
die_unless(d->ssl, "SSL_new failed");
|
||||||
|
|
||||||
@ -182,6 +199,37 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
return rlen;
|
return rlen;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GET_PEER_CERTIFICATE:
|
||||||
|
cert = SSL_get_peer_certificate(d->ssl);
|
||||||
|
if (cert == NULL)
|
||||||
|
{
|
||||||
|
b = driver_alloc_binary(1);
|
||||||
|
b->orig_bytes[0] = 1;
|
||||||
|
*rbuf = (char *)b;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
unsigned char *tmp_buf;
|
||||||
|
rlen = i2d_X509(cert, NULL);
|
||||||
|
if (rlen >= 0)
|
||||||
|
{
|
||||||
|
rlen++;
|
||||||
|
b = driver_alloc_binary(rlen);
|
||||||
|
b->orig_bytes[0] = 0;
|
||||||
|
tmp_buf = &b->orig_bytes[1];
|
||||||
|
i2d_X509(cert, &tmp_buf);
|
||||||
|
X509_free(cert);
|
||||||
|
*rbuf = (char *)b;
|
||||||
|
return rlen;
|
||||||
|
} else
|
||||||
|
X509_free(cert);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GET_VERIFY_RESULT:
|
||||||
|
b = driver_alloc_binary(1);
|
||||||
|
b->orig_bytes[0] = SSL_get_verify_result(d->ssl);
|
||||||
|
*rbuf = (char *)b;
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
b = driver_alloc_binary(1);
|
b = driver_alloc_binary(1);
|
||||||
|
Loading…
Reference in New Issue
Block a user