1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2015-2017. 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%% Purpose: CRL handling
22%%----------------------------------------------------------------------
23
24-module(ssl_crl).
25
26-include("ssl_alert.hrl").
27-include("ssl_internal.hrl").
28-include_lib("public_key/include/public_key.hrl").
29
30-export([trusted_cert_and_path/4]).
31
32trusted_cert_and_path(CRL, {SerialNumber, Issuer}, CertPath, {Db, DbRef}) ->
33    %% CRL issuer cert ID is known
34    case ssl_pkix_db:lookup_trusted_cert(Db, DbRef, SerialNumber, Issuer) of
35	undefined ->
36            %% But not found in our database
37	    search_certpath(CRL, CertPath, Db, DbRef);
38	{ok, #cert{otp=OtpCert}}  ->
39	    {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
40	    {ok, Root,  lists:reverse(Chain)}
41    end;
42trusted_cert_and_path(CRL, issuer_not_found, CertPath, {Db, DbRef}) ->
43    case search_certpath(CRL, CertPath, Db, DbRef) of
44	{error, unknown_ca} ->
45            Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
46            IsIssuerFun =
47                fun({_Key, CertCandidate}, Acc) ->
48                        verify_crl_issuer(CRL, CertCandidate, Issuer, Acc);
49                   (_, Acc) ->
50                        Acc
51                end,
52            case search_db(IsIssuerFun, Db, DbRef) of
53                {ok, OtpCert} ->
54                    {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
55                    {ok, Root, lists:reverse(Chain)};
56                {error, issuer_not_found} ->
57                    {error, unknown_ca}
58            end;
59        Result ->
60            Result
61    end.
62
63search_certpath(CRL, CertPath, Db, DbRef) ->
64    Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
65    IsIssuerFun =
66	fun(CertCandidate, Acc) ->
67		verify_crl_issuer(CRL, CertCandidate, Issuer, Acc)
68	end,
69    case find_issuer(IsIssuerFun, certpath, CertPath) of
70	{ok, OtpCert} ->
71	    {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
72	    {ok, Root, lists:reverse(Chain)};
73	{error, issuer_not_found} ->
74            {error, unknown_ca}
75    end.
76
77search_db(IsIssuerFun, _, {extracted, ExtractedCerts})->
78    find_issuer(IsIssuerFun, extracted, ExtractedCerts);
79search_db(IsIssuerFun, Db, DbRef) ->
80    find_issuer(IsIssuerFun, Db, DbRef).
81
82find_issuer(IsIssuerFun, certpath, Certs) ->
83    try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of
84        issuer_not_found ->
85            {error, issuer_not_found}
86    catch
87        {ok, _} = Result ->
88            Result
89    end;
90find_issuer(IsIssuerFun, extracted, CertsData) ->
91    Certs = [Entry || {decoded, Entry} <- CertsData],
92    try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of
93        issuer_not_found ->
94            {error, issuer_not_found}
95    catch
96        {ok, _} = Result ->
97            Result
98    end;
99find_issuer(IsIssuerFun, Db, _) ->
100    try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of
101        issuer_not_found ->
102            {error, issuer_not_found}
103    catch
104        {ok, _} = Result ->
105            Result
106    end.
107
108verify_crl_issuer(CRL, #cert{otp = OTPCertCandidate}, Issuer, NotIssuer) ->
109    TBSCert = OTPCertCandidate#'OTPCertificate'.tbsCertificate,
110    case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of
111	Issuer ->
112	    case public_key:pkix_crl_verify(CRL, OTPCertCandidate) of
113		true ->
114		    throw({ok, OTPCertCandidate});
115		false ->
116		    NotIssuer
117	    end;
118	_ ->
119	    NotIssuer
120    end.
121