1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%----------------------------------------------------------------------
22%% Purpose: Storage for trusted certificates
23%%----------------------------------------------------------------------
24
25-module(ssl_pkix_db).
26
27-include("ssl_internal.hrl").
28-include_lib("public_key/include/public_key.hrl").
29-include_lib("kernel/include/file.hrl").
30-include_lib("kernel/include/logger.hrl").
31
32-export([create/1, create_pem_cache/1,
33	 add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
34         refresh_trusted_certs/1,
35         refresh_trusted_certs/2,
36	 extract_trusted_certs/1,
37	 remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
38	 ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2,
39	 decode_pem_file/1, lookup/2]).
40
41%%====================================================================
42%% Internal application API
43%%====================================================================
44
45%%--------------------------------------------------------------------
46-spec create(atom()) -> [db_handle(),...].
47%%
48%% Description: Creates a new certificate db.
49%% Note: lookup_trusted_cert/4 may be called from any process but only
50%% the process that called create may call the other functions.
51%%--------------------------------------------------------------------
52create(PEMCacheName) ->
53    [%% Let connection process delete trusted certs
54     %% that can only belong to one connection. (Supplied directly
55     %% on DER format to ssl:connect/listen.)
56     ets:new(ssl_otp_cacertificate_db, [set, public]),
57     %% Let connection processes call ref_count/3 directly
58     {ets:new(ssl_otp_ca_file_ref, [set, public]),
59      ets:new(ssl_otp_ca_ref_file_mapping, [set, protected])
60     },
61     %% Lookups in named table owned by ssl_pem_cache process
62     PEMCacheName,
63     %% Default cache
64     {ets:new(ssl_otp_crl_cache, [set, protected]),
65      ets:new(ssl_otp_crl_issuer_mapping, [bag, protected])}
66    ].
67
68create_pem_cache(Name) ->
69    ets:new(Name, [named_table, set, protected]).
70
71%%--------------------------------------------------------------------
72-spec remove([db_handle()]) -> ok.
73%%
74%% Description: Removes database db
75%%--------------------------------------------------------------------
76remove(Dbs) ->
77    lists:foreach(fun({Db0, Db1})  ->
78			  true = ets:delete(Db0),
79			  true = ets:delete(Db1);
80		     (undefined) ->
81			  ok;
82                     (Name) when is_atom(Name) ->
83                          NormalName = ssl_pem_cache:name(normal),
84                          DistName = ssl_pem_cache:name(dist),
85                          case Name of
86                              NormalName ->
87                                  ok;
88                              DistName ->
89                                  ok;
90                              _ ->
91                                  true = ets:delete(Name)
92                          end;
93		     (Db) ->
94			  true = ets:delete(Db)
95		  end, Dbs).
96
97%%--------------------------------------------------------------------
98-spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) ->
99				 undefined | {ok, public_key:combined_cert()}.
100
101%%
102%% Description: Retrives the trusted certificate identified by
103%% <SerialNumber, Issuer>. Ref is used as it is specified
104%% for each connection which certificates are trusted.
105%%--------------------------------------------------------------------
106lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) when is_reference(Ref) ->
107    case lookup({Ref, SerialNumber, Issuer}, DbHandle) of
108	undefined ->
109	    undefined;
110	[Certs] ->
111	    {ok, Certs}
112    end;
113lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) ->
114    try
115	[throw(Cert)
116	 || {decoded, {{_Ref,CertSerial,CertIssuer}, Cert}} <- Certs,
117	    CertSerial =:= SerialNumber, CertIssuer =:= Issuer],
118	undefined
119    catch
120	Cert ->
121	    {ok, Cert}
122    end.
123
124%%--------------------------------------------------------------------
125-spec add_trusted_certs(pid(), {erlang:timestamp(), string()} |
126			{der, list()}, [db_handle()]) -> {ok, [db_handle()]}.
127%%
128%% Description: Adds the trusted certificates from file <File> to the
129%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
130%% together with the cert serialnumber and issuer.
131%%--------------------------------------------------------------------
132add_trusted_certs(_Pid, {extracted, _} = Certs, _) ->
133    {ok, Certs};
134
135add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) ->
136    NewRef = make_ref(),
137    add_certs_from_der(DerList, NewRef, CertDb),
138    {ok, NewRef};
139
140add_trusted_certs(_Pid, File, [ _, {RefDb, FileMapDb} | _] = Db) ->
141    case lookup(File, FileMapDb) of
142	[Ref] ->
143	    ref_count(Ref, RefDb, 1),
144	    {ok, Ref};
145	undefined ->
146	    new_trusted_cert_entry(File, Db)
147    end.
148
149refresh_trusted_certs(File, [CertsDb, {_, FileMapDb} | _]) ->
150    case lookup(File, FileMapDb) of
151        [Ref] ->
152            {ok, Content} = decode_pem_file(File),
153            remove_trusted_certs(Ref, CertsDb),
154            add_certs_from_pem(Content, Ref, CertsDb);
155        undefined ->
156            ok
157    end.
158refresh_trusted_certs([_, {_, FileMapDb} | _] = Db) ->
159    Refresh = fun({File, _}, Acc) ->
160                      refresh_trusted_certs(File, Db),
161                      Acc
162              end,
163    foldl(Refresh, refresh, FileMapDb).
164
165extract_trusted_certs({der, DerList}) ->
166    {ok, {extracted, certs_from_der(DerList)}};
167extract_trusted_certs({der_otp, DerList}) ->
168    {ok, {extracted, certs_from_der(DerList)}};
169extract_trusted_certs(File) ->
170    case file:read_file(File) of
171        {ok, PemBin} ->
172            Content = public_key:pem_decode(PemBin),
173            DerList = [Cert || {'Certificate', Cert, not_encrypted} <- Content],
174            {ok, {extracted, certs_from_der(DerList)}};
175        Error ->
176            %% Have to simulate a failure happening in a server for
177            %% external handlers.
178            {error, {badmatch, Error}}
179    end.
180
181-spec decode_pem_file(binary()) -> {ok, term()} | {error, term()}.
182decode_pem_file(File) ->
183    case file:read_file(File) of
184        {ok, PemBin} ->
185            Content = public_key:pem_decode(PemBin),
186            {ok, Content};
187        Error ->
188            %% Have to simulate a failure happening in a server for
189            %% external handlers.
190            {error, {badmatch, Error}}
191    end.
192
193%%--------------------------------------------------------------------
194-spec remove_trusted_certs(reference(), db_handle()) -> ok.
195%%
196%% Description: Removes all trusted certificates refernced by <Ref>.
197%%--------------------------------------------------------------------
198remove_trusted_certs(Ref, CertsDb) ->
199    remove_certs(Ref, CertsDb).
200
201%%--------------------------------------------------------------------
202-spec remove(term(), db_handle()) -> ok.
203%%
204%% Description: Removes an element in a <Db>.
205%%--------------------------------------------------------------------
206remove(Key, Db) ->
207    ets:delete(Db, Key),
208    ok.
209
210%%--------------------------------------------------------------------
211-spec remove(term(), term(), db_handle()) -> ok.
212%%
213%% Description: Removes an element in a <Db>.
214%%--------------------------------------------------------------------
215remove(Key, Data, Db) ->
216    ets:delete_object(Db, {Key, Data}),
217    ok.
218
219%%--------------------------------------------------------------------
220-spec lookup(term(), db_handle()) -> [term()] | undefined.
221%%
222%% Description: Looks up an element in a <Db>.
223%%--------------------------------------------------------------------
224lookup(Key, Db) ->
225    case ets:lookup(Db, Key) of
226	[] ->
227	    undefined;
228	Contents  ->
229	    Pick = fun({_, Data}) -> Data;
230		      ({_,_,Data}) -> Data
231		   end,
232	    [Pick(Data) || Data <- Contents]
233    end.
234%%--------------------------------------------------------------------
235-spec foldl(fun((_,_) -> term()), term(), db_handle()) -> term().
236%%
237%% Description: Calls Fun(Elem, AccIn) on successive elements of the
238%% cache, starting with AccIn == Acc0. Fun/2 must return a new
239%% accumulator which is passed to the next call. The function returns
240%% the final value of the accumulator. Acc0 is returned if the certifate
241%% db is empty.
242%%--------------------------------------------------------------------
243foldl(Fun, Acc0, Cache) ->
244    ets:foldl(Fun, Acc0, Cache).
245
246
247select_cert_by_issuer(Cache, Issuer) ->
248    ets:select(Cache, [{{{'_','_', Issuer},{'_', '$1'}},[],['$$']}]).
249
250%%--------------------------------------------------------------------
251-spec ref_count(term(), db_handle(), integer()) -> integer().
252%%
253%% Description: Updates a reference counter in a <Db>.
254%%--------------------------------------------------------------------
255ref_count({extracted, _}, _Db, _N) ->
256    not_cached;
257ref_count(Key, {Db, _}, N) ->
258    ref_count(Key, Db, N);
259ref_count(Key, Db, N) ->
260    ets:update_counter(Db,Key,N).
261
262%%--------------------------------------------------------------------
263-spec clear(db_handle()) -> ok.
264%%
265%% Description: Clears the cache
266%%--------------------------------------------------------------------
267clear(Db) ->
268    true = ets:delete_all_objects(Db),
269    ok.
270
271%%--------------------------------------------------------------------
272-spec db_size(db_handle()) -> integer().
273%%
274%% Description: Returns the size of the db
275%%--------------------------------------------------------------------
276db_size(Db) ->
277    ets:info(Db, size).
278
279%%--------------------------------------------------------------------
280-spec insert(Key::term(), Data::term(), Db::db_handle()) -> ok.
281%%
282%% Description: Inserts data into <Db>
283%%--------------------------------------------------------------------
284insert(Key, Data, Db) ->
285    true = ets:insert(Db, {Key, Data}),
286    ok.
287
288%%--------------------------------------------------------------------
289%%% Internal functions
290%%--------------------------------------------------------------------
291init_ref_db(Ref, File, {RefDb, FileMapDb}) ->
292    true = ets:insert(RefDb, {Ref, 1}),
293    true = ets:insert(FileMapDb, {File, Ref}).
294
295remove_certs(Ref, CertsDb) ->
296    true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}),
297    ok.
298
299add_certs_from_der(DerList, Ref, CertsDb) ->
300    Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
301    [Add(Cert) || Cert <- DerList],
302    ok.
303
304certs_from_der(DerList) ->
305    Ref = make_ref(),
306    [Decoded || Cert <- DerList,
307		Decoded <- [decode_certs(Ref, Cert)],
308		Decoded =/= undefined].
309
310add_certs_from_pem(PemEntries, Ref, CertsDb) ->
311    Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
312    [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
313    ok.
314
315add_certs(Cert, Ref, CertsDb) ->
316    try
317	 {decoded, {Key, Val}} = decode_certs(Ref, Cert),
318	 insert(Key, Val, CertsDb)
319    catch
320	error:_ ->
321	    ok
322    end.
323
324decode_certs(Ref, #cert{otp=ErlCert} = Cert) ->
325    TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
326    SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
327    Issuer = public_key:pkix_normalize_name(
328               TBSCertificate#'OTPTBSCertificate'.issuer),
329    {decoded, {{Ref, SerialNumber, Issuer}, Cert}};
330decode_certs(Ref, Der) ->
331    try public_key:pkix_decode_cert(Der, otp) of
332        ErlCert ->
333            decode_certs(Ref, #cert{der=Der, otp=ErlCert})
334    catch error:_ ->
335	    ?LOG_NOTICE("SSL WARNING: Ignoring a CA cert as "
336                        "it could not be correctly decoded.~n"),
337	    undefined
338    end.
339
340new_trusted_cert_entry(File, [CertsDb, RefsDb, _ | _]) ->
341    case decode_pem_file(File) of
342        {ok, Content} ->
343            Ref = make_ref(),
344            init_ref_db(Ref, File, RefsDb),
345            ok = ssl_pem_cache:insert(File, Content),
346            add_certs_from_pem(Content, Ref, CertsDb),
347            {ok, Ref};
348        Error ->
349            Error
350    end.
351
352add_crls([_,_,_, {_, Mapping} | _], ?NO_DIST_POINT, CRLs) ->
353    [add_crls(CRL, Mapping) || CRL <- CRLs];
354add_crls([_,_,_, {Cache, Mapping} | _], Path, CRLs) ->
355    insert(Path, CRLs, Cache),
356    [add_crls(CRL, Mapping) || CRL <- CRLs].
357
358add_crls(CRL, Mapping) ->
359    insert(crl_issuer(CRL), CRL, Mapping).
360
361remove_crls([_,_,_, {_, Mapping} | _], {?NO_DIST_POINT, CRLs}) ->
362    [rm_crls(CRL, Mapping) || CRL <- CRLs];
363
364remove_crls([_,_,_, {Cache, Mapping} | _], Path) ->
365    case lookup(Path, Cache) of
366	undefined ->
367	    ok;
368	CRLs ->
369	    remove(Path, Cache),
370	    [rm_crls(CRL, Mapping) || CRL <- CRLs]
371    end.
372
373rm_crls(CRL, Mapping) ->
374   remove(crl_issuer(CRL), CRL, Mapping).
375
376crl_issuer(DerCRL) ->
377    CRL = public_key:der_decode('CertificateList', DerCRL),
378    TBSCRL = CRL#'CertificateList'.tbsCertList,
379    TBSCRL#'TBSCertList'.issuer.
380
381