aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_certificate_db.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_certificate_db.erl')
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl219
1 files changed, 219 insertions, 0 deletions
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
new file mode 100644
index 0000000000..decc6c9fea
--- /dev/null
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -0,0 +1,219 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose: Storage for trused certificats
+%%----------------------------------------------------------------------
+
+-module(ssl_certificate_db).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-export([create/0, remove/1, add_trusted_certs/3,
+ remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
+ cache_pem_file/3]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: create() -> Db
+%% Db = term() - Reference to the crated database
+%%
+%% Description: Creates a new certificate db.
+%% Note: lookup_trusted_cert/3 may be called from any process but only
+%% the process that called create may call the other functions.
+%%--------------------------------------------------------------------
+create() ->
+ [ets:new(certificate_db_name(), [named_table, set, protected]),
+ ets:new(ssl_file_to_ref, [named_table, set, protected]),
+ ets:new(ssl_pid_to_file, [bag, private])].
+
+%%--------------------------------------------------------------------
+%% Function: delete(Db) -> _
+%% Db = Database refererence as returned by create/0
+%%
+%% Description: Removes database db
+%%--------------------------------------------------------------------
+remove(Dbs) ->
+ lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs).
+
+%%--------------------------------------------------------------------
+%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert}
+%% Ref = ref()
+%% SerialNumber = integer()
+%% Issuer = {rdnSequence, IssuerAttrs}
+%% BinCert = binary()
+%%
+%% Description: Retrives the trusted certificate identified by
+%% <SerialNumber, Issuer>. Ref is used as it is specified
+%% for each connection which certificates are trusted.
+%%--------------------------------------------------------------------
+lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
+ case lookup({Ref, SerialNumber, Issuer}, certificate_db_name()) of
+ undefined ->
+ undefined;
+ [Certs] ->
+ {ok, Certs}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref}
+%% Pid = pid()
+%% File = string()
+%% Db = Database refererence as returned by create/0
+%% Ref = ref()
+%%
+%% Description: Adds the trusted certificates from file <File> to the
+%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
+%% together with the cert serialnumber and issuer.
+%%--------------------------------------------------------------------
+add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
+ Ref = case lookup(File, FileToRefDb) of
+ undefined ->
+ NewRef = make_ref(),
+ add_certs_from_file(File, NewRef, CertsDb),
+ insert(File, NewRef, 1, FileToRefDb),
+ NewRef;
+ [OldRef] ->
+ ref_count(File,FileToRefDb,1),
+ OldRef
+ end,
+ insert(Pid, File, PidToFileDb),
+ {ok, Ref}.
+
+%%--------------------------------------------------------------------
+%% Function: cache_pem_file(Pid, File, Db) -> FileContent
+%%
+%% Description: Cache file as binary in DB
+%%--------------------------------------------------------------------
+cache_pem_file(Pid, File, [_CertsDb, FileToRefDb, PidToFileDb]) ->
+ try ref_count(File, FileToRefDb,1)
+ catch _:_ ->
+ {ok, Content} = public_key:pem_to_der(File),
+ insert(File,Content,1,FileToRefDb)
+ end,
+ insert(Pid, File, PidToFileDb),
+ {ok, FileToRefDb}.
+
+%%--------------------------------------------------------------------
+%% Function: remove_trusted_certs(Pid, Db) -> _
+%%
+%% Description: Removes trusted certs originating from
+%% the file associated to Pid from the runtime database.
+%%--------------------------------------------------------------------
+remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
+ Files = lookup(Pid, PidToFileDb),
+ delete(Pid, PidToFileDb),
+ Clear = fun(File) ->
+ case ref_count(File, FileToRefDb, -1) of
+ 0 ->
+ case lookup(File, FileToRefDb) of
+ [Ref] when is_reference(Ref) ->
+ remove_certs(Ref, CertsDb);
+ _ -> ok
+ end,
+ delete(File, FileToRefDb);
+ _ ->
+ ok
+ end
+ end,
+ case Files of
+ undefined -> ok;
+ _ ->
+ [Clear(File) || File <- Files],
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates
+%%
+%% Candidate
+%%
+%%
+%% Description: If a certificat does not define its issuer through
+%% the extension 'ce-authorityKeyIdentifier' we can
+%% try to find the issuer in the database over known
+%% certificates.
+%%--------------------------------------------------------------------
+issuer_candidate(no_candidate) ->
+ Db = certificate_db_name(),
+ case ets:first(Db) of
+ '$end_of_table' ->
+ no_more_candidates;
+ Key ->
+ [Cert] = lookup(Key, Db),
+ {Key, Cert}
+ end;
+
+issuer_candidate(PrevCandidateKey) ->
+ Db = certificate_db_name(),
+ case ets:next(Db, PrevCandidateKey) of
+ '$end_of_table' ->
+ no_more_candidates;
+ Key ->
+ [Cert] = lookup(Key, Db),
+ {Key, Cert}
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+certificate_db_name() ->
+ ssl_otp_certificate_db.
+
+insert(Key, Data, Db) ->
+ true = ets:insert(Db, {Key, Data}).
+
+insert(Key, Data, Count, Db) ->
+ true = ets:insert(Db, {Key, Count, Data}).
+
+ref_count(Key, Db,N) ->
+ ets:update_counter(Db,Key,N).
+
+delete(Key, Db) ->
+ true = ets:delete(Db, Key).
+
+lookup(Key, Db) ->
+ case ets:lookup(Db, Key) of
+ [] ->
+ undefined;
+ Contents ->
+ Pick = fun({_, Data}) -> Data;
+ ({_,_,Data}) -> Data
+ end,
+ [Pick(Data) || Data <- Contents]
+ end.
+
+remove_certs(Ref, CertsDb) ->
+ ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
+
+add_certs_from_file(File, Ref, CertsDb) ->
+ Decode = fun(Cert) ->
+ {ok, ErlCert} = public_key:pkix_decode_cert(Cert, otp),
+ TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
+ SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
+ Issuer = public_key:pkix_normalize_general_name(
+ TBSCertificate#'OTPTBSCertificate'.issuer),
+ insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb)
+ end,
+ {ok,Der} = public_key:pem_to_der(File),
+ [Decode(Cert) || {cert, Cert, not_encrypted} <- Der].
+