]> granicus.if.org Git - ejabberd/commitdiff
* src/ejabberd_s2s_out.erl: Support for STARTTLS+SASL EXTERNAL
authorAlexey Shchepin <alexey@process-one.net>
Thu, 3 Nov 2005 05:04:54 +0000 (05:04 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Thu, 3 Nov 2005 05:04:54 +0000 (05:04 +0000)
* 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

ChangeLog
src/ejabberd_s2s_in.erl
src/ejabberd_s2s_out.erl
src/tls/Makefile.in
src/tls/XmppAddr.asn1 [new file with mode: 0644]
src/tls/tls.erl
src/tls/tls_drv.c

index 662fc2d44d56c7bf02c44d983f4023225529934d..3587ad0ee50fe90094f044e9ae89029722ae0944 100644 (file)
--- a/ChangeLog
+++ b/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>
 
        * src/mod_disco.erl: Minor fix
index d2b616751752a4f25f76bee185ef718ca1c80146..79ae93387607b887dca308b1869a82302c12c4a8 100644 (file)
@@ -29,6 +29,9 @@
 
 -include("ejabberd.hrl").
 -include("jlib.hrl").
+%-include_lib("ssl/pkix/SSL-PKIX.hrl").
+-include_lib("ssl/pkix/PKIX1Explicit88.hrl").
+-include("tls/XmppAddr.hrl").
 
 -define(DICT, dict).
 
@@ -40,6 +43,8 @@
                tls = false,
                tls_enabled = false,
                tls_options = [],
+               authenticated = false,
+               auth_domain,
                connections = ?DICT:new(),
                timer}).
 
@@ -131,18 +136,50 @@ init([{SockMod, Socket}, Opts]) ->
 %%----------------------------------------------------------------------
 
 wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
-    % TODO
     case {xml:get_attr_s("xmlns", Attrs),
          xml:get_attr_s("xmlns:db", Attrs),
          xml:get_attr_s("version", Attrs) == "1.0"} of
        {"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'")),
+           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,
                         {xmlelement, "stream:features", [],
-                         [{xmlelement, "starttls",
-                           [{"xmlns", ?NS_TLS}], []}]}),
+                         SASL ++ StartTLS}),
            {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", _} ->
            send_text(StateData, ?STREAM_HEADER("")),
            {next_state, stream_established, StateData};
@@ -185,6 +222,60 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
                             streamid = new_id(),
                             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)
     end;
@@ -252,18 +343,38 @@ stream_established({xmlstreamelement, El}, StateData) ->
                (To /= error) and (From /= error) ->
                    LFrom = From#jid.lserver,
                    LTo = To#jid.lserver,
-                   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 ->
+                   if
+                       StateData#state.authenticated ->
+                           case (LFrom == StateData#state.auth_domain)
+                               andalso
+                               lists:member(
+                                 LTo,
+                                 ejabberd_router:dirty_get_all_domains()) of
+                               true ->
+                                   if ((Name == "iq") or
+                                       (Name == "message") or
+                                       (Name == "presence")) ->
+                                           ejabberd_router:route(From, To, El);
+                                      true ->
+                                           error
+                                   end;
+                               false ->
                                    error
                            end;
-                       _ ->
-                           error
+                       true ->
+                           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;
                true ->
                    error
@@ -365,7 +476,7 @@ handle_info({send_text, Text}, StateName, StateData) ->
     send_text(StateData, Text),
     {next_state, StateName, StateData};
 
-handle_info({timeout, Timer, _}, StateName,
+handle_info({timeout, Timer, _}, _StateName,
            #state{timer = Timer} = StateData) ->
     {stop, normal, StateData};
 
@@ -428,4 +539,51 @@ is_key_packet(_) ->
     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)).
+
+
 
index 0b3d46f197aa87621688594f8155a64b81e53e63..0384297d2e74c79ffe723b50043461326c636c39 100644 (file)
@@ -21,6 +21,7 @@
         wait_for_stream/2,
         wait_for_validation/2,
         wait_for_features/2,
+        wait_for_auth_result/2,
         wait_for_starttls_proceed/2,
         stream_established/2,
         handle_event/3,
@@ -40,6 +41,8 @@
                tls_required = false,
                tls_enabled = false,
                tls_options = [],
+               authenticated = false,
+               try_auth = true,
                myname, server, queue,
                new = false, verify = false,
                timer}).
@@ -276,23 +279,57 @@ wait_for_validation(closed, StateData) ->
 wait_for_features({xmlstreamelement, El}, StateData) ->
     case El of
        {xmlelement, "stream:features", _Attrs, Els} ->
-           {StartTLS, StartTLSRequired} =
+           {SASLEXT, StartTLS, StartTLSRequired} =
                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
                              ?NS_TLS ->
                                  Req = case xml:get_subtag(El1, "required") of
                                            {xmlelement, _, _, _} -> true;
                                            false -> false
                                        end,
-                                 {true, Req};
+                                 {SEXT, true, Req};
                              _ ->
                                  Acc
                          end;
                     (_, Acc) ->
                          Acc
-                 end, {false, false}, Els),
+                 end, {false, false, false}, Els),
            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
                (not StateData#state.tls_enabled) ->
                    StateData#state.receiver ! {change_timeout, 100},
@@ -333,6 +370,66 @@ wait_for_features(closed, 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) ->
     case El of
        {xmlelement, "proceed", Attrs, _Els} ->
@@ -351,12 +448,15 @@ wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
                                                   streamid = new_id(),
                                                   tls_enabled = true
                                                  },
-                   R = send_text(NewStateData,
-                                 io_lib:format(?STREAM_HEADER,
-                                               [StateData#state.server,
-                                                " version='1.0'"])),
+                   send_text(NewStateData,
+                             io_lib:format(?STREAM_HEADER,
+                                           [StateData#state.server,
+                                            " version='1.0'"])),
                    {next_state, wait_for_stream, NewStateData};
                _ ->
+                   send_text(StateData,
+                             xml:element_to_string(?SERR_BAD_FORMAT) ++
+                             ?STREAM_TRAILER),
                    {stop, normal, StateData}
            end;
        _ ->
index e0ebd5d4f1edf681ba464100e1ed5ffe6f632d00..936747fe42bd15513db10898d60ccdc8f7524b72 100644 (file)
@@ -12,14 +12,18 @@ ERLSHLIBS = ../tls_drv.so
 
 OUTDIR = ..
 EFLAGS = -I .. -pz ..
+ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
 OBJS   = \
-       $(OUTDIR)/tls.beam
+       $(OUTDIR)/tls.beam $(OUTDIR)/XmppAddr.beam
 
 all:    $(OBJS) $(ERLSHLIBS)
 
 $(OUTDIR)/%.beam:       %.erl
        @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
 
+%.erl: %.asn1
+       erlc $(ASN_FLAGS) $<
+
 #all:  $(ERLSHLIBS)
 #      erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
 
diff --git a/src/tls/XmppAddr.asn1 b/src/tls/XmppAddr.asn1
new file mode 100644 (file)
index 0000000..14f350d
--- /dev/null
@@ -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
index cdd00570b9c26d38b40105a9a2af27a9cd5c3281..7290160e11397c32f3ed0c16b79fa58a4398aa5b 100644 (file)
@@ -17,6 +17,8 @@
         send/2,
         recv/2, recv/3, recv_data/2,
         close/1,
+        get_peer_certificate/1,
+        get_verify_result/1,
         test/0]).
 
 %% Internal exports, call-back functions.
@@ -33,6 +35,8 @@
 -define(SET_DECRYPTED_OUTPUT, 4).
 -define(GET_ENCRYPTED_OUTPUT, 5).
 -define(GET_DECRYPTED_INPUT,  6).
+-define(GET_PEER_CERTIFICATE, 7).
+-define(GET_VERIFY_RESULT,    8).
 
 -record(tlssock, {tcpsock, tlsport}).
 
@@ -69,15 +73,16 @@ handle_call(_, _, State) ->
 handle_cast(_, State) ->
     {noreply, State}.
 
-handle_info({'EXIT', Pid, Reason}, Port) ->
-    {noreply, Port};
-
 handle_info({'EXIT', Port, Reason}, Port) ->
     {stop, {port_died, Reason}, Port};
+
+handle_info({'EXIT', _Pid, _Reason}, Port) ->
+    {noreply, Port};
+
 handle_info(_, State) ->
     {noreply, State}.
 
-code_change(OldVsn, State, Extra) ->
+code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
 terminate(_Reason, Port) ->
@@ -115,7 +120,7 @@ tls_to_tcp(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
 
 recv(Socket, Length) ->
     recv(Socket, Length, infinity).
-recv(#tlssock{tcpsock = TCPSocket, tlsport = Port} = TLSSock,
+recv(#tlssock{tcpsock = TCPSocket} = TLSSock,
      Length, Timeout) ->
     case gen_tcp:recv(TCPSocket, Length, Timeout) of
        {ok, Packet} ->
@@ -133,6 +138,7 @@ recv_data(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
                        <<0, Out/binary>> ->
                            case gen_tcp:send(TCPSocket, Out) of
                                ok ->
+                                   %io:format("IN: ~p~n", [{TCPSocket, binary_to_list(In)}]),
                                    {ok, In};
                                Error ->
                                    Error
@@ -150,6 +156,7 @@ recv_data(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
 send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
     case port_control(Port, ?SET_DECRYPTED_OUTPUT, Packet) of
        <<0>> ->
+           %io:format("OUT: ~p~n", [{TCPSocket, lists:flatten(Packet)}]),
            case port_control(Port, ?GET_ENCRYPTED_OUTPUT, []) of
                <<0, Out/binary>> ->
                    gen_tcp:send(TCPSocket, Out);
@@ -159,8 +166,12 @@ send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
        <<1, Error/binary>> ->
            {error, binary_to_list(Error)};
        <<2>> -> % Dirty hack
-           receive after 100 -> ok end,
-           send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet)
+           receive
+               {timeout, _Timer, _} ->
+                   {error, timeout}
+           after 100 ->
+                   send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet)
+           end
     end.
 
 
@@ -168,6 +179,23 @@ close(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
     gen_tcp:close(TCPSocket),
     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() ->
     case erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv) of
index 608830ffbeb1a6f204baaf556e98ec0d5dc9bf30..742eda8f02b276cfe6c16a617dac162fa0375933 100644 (file)
@@ -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_CONNECT 2
 #define SET_ENCRYPTED_INPUT  3
 #define SET_DECRYPTED_OUTPUT 4
 #define GET_ENCRYPTED_OUTPUT 5
 #define GET_DECRYPTED_INPUT  6
+#define GET_PEER_CERTIFICATE 7
+#define GET_VERIFY_RESULT    8
 
 
 #define die_unless(cond, errstr)                               \
@@ -75,6 +82,7 @@ static int tls_drv_control(ErlDrvData handle,
    int res;
    int size;
    ErlDrvBinary *b;
+   X509 *cert;
 
    switch (command)
    {
@@ -92,6 +100,15 @@ static int tls_drv_control(ErlDrvData handle,
         res = SSL_CTX_check_private_key(d->ctx);
         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);
         die_unless(d->ssl, "SSL_new failed");
 
@@ -182,6 +199,37 @@ static int tls_drv_control(ErlDrvData handle,
            return rlen;
         }
         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);