Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / docker / notary / trustpinning / certs.go
1 package trustpinning
2
3 import (
4         "crypto/x509"
5         "errors"
6         "fmt"
7         "strings"
8
9         "github.com/Sirupsen/logrus"
10         "github.com/docker/notary/tuf/data"
11         "github.com/docker/notary/tuf/signed"
12         "github.com/docker/notary/tuf/utils"
13 )
14
15 // ErrValidationFail is returned when there is no valid trusted certificates
16 // being served inside of the roots.json
17 type ErrValidationFail struct {
18         Reason string
19 }
20
21 // ErrValidationFail is returned when there is no valid trusted certificates
22 // being served inside of the roots.json
23 func (err ErrValidationFail) Error() string {
24         return fmt.Sprintf("could not validate the path to a trusted root: %s", err.Reason)
25 }
26
27 // ErrRootRotationFail is returned when we fail to do a full root key rotation
28 // by either failing to add the new root certificate, or delete the old ones
29 type ErrRootRotationFail struct {
30         Reason string
31 }
32
33 // ErrRootRotationFail is returned when we fail to do a full root key rotation
34 // by either failing to add the new root certificate, or delete the old ones
35 func (err ErrRootRotationFail) Error() string {
36         return fmt.Sprintf("could not rotate trust to a new trusted root: %s", err.Reason)
37 }
38
39 func prettyFormatCertIDs(certs map[string]*x509.Certificate) string {
40         ids := make([]string, 0, len(certs))
41         for id := range certs {
42                 ids = append(ids, id)
43         }
44         return strings.Join(ids, ", ")
45 }
46
47 /*
48 ValidateRoot receives a new root, validates its correctness and attempts to
49 do root key rotation if needed.
50
51 First we check if we have any trusted certificates for a particular GUN in
52 a previous root, if we have one. If the previous root is not nil and we find
53 certificates for this GUN, we've already seen this repository before, and
54 have a list of trusted certificates for it. In this case, we use this list of
55 certificates to attempt to validate this root file.
56
57 If the previous validation succeeds, we check the integrity of the root by
58 making sure that it is validated by itself. This means that we will attempt to
59 validate the root data with the certificates that are included in the root keys
60 themselves.
61
62 However, if we do not have any current trusted certificates for this GUN, we
63 check if there are any pinned certificates specified in the trust_pinning section
64 of the notary client config.  If this section specifies a Certs section with this
65 GUN, we attempt to validate that the certificates present in the downloaded root
66 file match the pinned ID.
67
68 If the Certs section is empty for this GUN, we check if the trust_pinning
69 section specifies a CA section specified in the config for this GUN.  If so, we check
70 that the specified CA is valid and has signed a certificate included in the downloaded
71 root file.  The specified CA can be a prefix for this GUN.
72
73 If both the Certs and CA configs do not match this GUN, we fall back to the TOFU
74 section in the config: if true, we trust certificates specified in the root for
75 this GUN. If later we see a different certificate for that certificate, we return
76 an ErrValidationFailed error.
77
78 Note that since we only allow trust data to be downloaded over an HTTPS channel
79 we are using the current public PKI to validate the first download of the certificate
80 adding an extra layer of security over the normal (SSH style) trust model.
81 We shall call this: TOFUS.
82
83 Validation failure at any step will result in an ErrValidationFailed error.
84 */
85 func ValidateRoot(prevRoot *data.SignedRoot, root *data.Signed, gun string, trustPinning TrustPinConfig) (*data.SignedRoot, error) {
86         logrus.Debugf("entered ValidateRoot with dns: %s", gun)
87         signedRoot, err := data.RootFromSigned(root)
88         if err != nil {
89                 return nil, err
90         }
91
92         rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
93         if err != nil {
94                 return nil, err
95         }
96
97         // Retrieve all the leaf and intermediate certificates in root for which the CN matches the GUN
98         allLeafCerts, allIntCerts := parseAllCerts(signedRoot)
99         certsFromRoot, err := validRootLeafCerts(allLeafCerts, gun, true)
100         validIntCerts := validRootIntCerts(allIntCerts)
101
102         if err != nil {
103                 logrus.Debugf("error retrieving valid leaf certificates for: %s, %v", gun, err)
104                 return nil, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
105         }
106
107         logrus.Debugf("found %d leaf certs, of which %d are valid leaf certs for %s", len(allLeafCerts), len(certsFromRoot), gun)
108
109         // If we have a previous root, let's try to use it to validate that this new root is valid.
110         havePrevRoot := prevRoot != nil
111         if havePrevRoot {
112                 // Retrieve all the trusted certificates from our previous root
113                 // Note that we do not validate expiries here since our originally trusted root might have expired certs
114                 allTrustedLeafCerts, allTrustedIntCerts := parseAllCerts(prevRoot)
115                 trustedLeafCerts, err := validRootLeafCerts(allTrustedLeafCerts, gun, false)
116                 if err != nil {
117                         return nil, &ErrValidationFail{Reason: "could not retrieve trusted certs from previous root role data"}
118                 }
119
120                 // Use the certificates we found in the previous root for the GUN to verify its signatures
121                 // This could potentially be an empty set, in which case we will fail to verify
122                 logrus.Debugf("found %d valid root leaf certificates for %s: %s", len(trustedLeafCerts), gun,
123                         prettyFormatCertIDs(trustedLeafCerts))
124
125                 // Extract the previous root's threshold for signature verification
126                 prevRootRoleData, ok := prevRoot.Signed.Roles[data.CanonicalRootRole]
127                 if !ok {
128                         return nil, &ErrValidationFail{Reason: "could not retrieve previous root role data"}
129                 }
130                 err = signed.VerifySignatures(
131                         root, data.BaseRole{Keys: utils.CertsToKeys(trustedLeafCerts, allTrustedIntCerts), Threshold: prevRootRoleData.Threshold})
132                 if err != nil {
133                         logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
134                         return nil, &ErrRootRotationFail{Reason: "failed to validate data with current trusted certificates"}
135                 }
136                 // Clear the IsValid marks we could have received from VerifySignatures
137                 for i := range root.Signatures {
138                         root.Signatures[i].IsValid = false
139                 }
140         }
141
142         // Regardless of having a previous root or not, confirm that the new root validates against the trust pinning
143         logrus.Debugf("checking root against trust_pinning config", gun)
144         trustPinCheckFunc, err := NewTrustPinChecker(trustPinning, gun, !havePrevRoot)
145         if err != nil {
146                 return nil, &ErrValidationFail{Reason: err.Error()}
147         }
148
149         validPinnedCerts := map[string]*x509.Certificate{}
150         for id, cert := range certsFromRoot {
151                 logrus.Debugf("checking trust-pinning for cert: %s", id)
152                 if ok := trustPinCheckFunc(cert, validIntCerts[id]); !ok {
153                         logrus.Debugf("trust-pinning check failed for cert: %s", id)
154                         continue
155                 }
156                 validPinnedCerts[id] = cert
157         }
158         if len(validPinnedCerts) == 0 {
159                 return nil, &ErrValidationFail{Reason: "unable to match any certificates to trust_pinning config"}
160         }
161         certsFromRoot = validPinnedCerts
162
163         // Validate the integrity of the new root (does it have valid signatures)
164         // Note that certsFromRoot is guaranteed to be unchanged only if we had prior cert data for this GUN or enabled TOFUS
165         // If we attempted to pin a certain certificate or CA, certsFromRoot could have been pruned accordingly
166         err = signed.VerifySignatures(root, data.BaseRole{
167                 Keys: utils.CertsToKeys(certsFromRoot, validIntCerts), Threshold: rootRole.Threshold})
168         if err != nil {
169                 logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
170                 return nil, &ErrValidationFail{Reason: "failed to validate integrity of roots"}
171         }
172
173         logrus.Debugf("root validation succeeded for %s", gun)
174         // Call RootFromSigned to make sure we pick up on the IsValid markings from VerifySignatures
175         return data.RootFromSigned(root)
176 }
177
178 // validRootLeafCerts returns a list of possibly (if checkExpiry is true) non-expired, non-sha1 certificates
179 // found in root whose Common-Names match the provided GUN. Note that this
180 // "validity" alone does not imply any measure of trust.
181 func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string, checkExpiry bool) (map[string]*x509.Certificate, error) {
182         validLeafCerts := make(map[string]*x509.Certificate)
183
184         // Go through every leaf certificate and check that the CN matches the gun
185         for id, cert := range allLeafCerts {
186                 // Validate that this leaf certificate has a CN that matches the exact gun
187                 if cert.Subject.CommonName != gun {
188                         logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s",
189                                 cert.Subject.CommonName, gun)
190                         continue
191                 }
192                 // Make sure the certificate is not expired if checkExpiry is true
193                 // and warn if it hasn't expired yet but is within 6 months of expiry
194                 if err := utils.ValidateCertificate(cert, checkExpiry); err != nil {
195                         logrus.Debugf("%s is invalid: %s", id, err.Error())
196                         continue
197                 }
198
199                 validLeafCerts[id] = cert
200         }
201
202         if len(validLeafCerts) < 1 {
203                 logrus.Debugf("didn't find any valid leaf certificates for %s", gun)
204                 return nil, errors.New("no valid leaf certificates found in any of the root keys")
205         }
206
207         logrus.Debugf("found %d valid leaf certificates for %s: %s", len(validLeafCerts), gun,
208                 prettyFormatCertIDs(validLeafCerts))
209         return validLeafCerts, nil
210 }
211
212 // validRootIntCerts filters the passed in structure of intermediate certificates to only include non-expired, non-sha1 certificates
213 // Note that this "validity" alone does not imply any measure of trust.
214 func validRootIntCerts(allIntCerts map[string][]*x509.Certificate) map[string][]*x509.Certificate {
215         validIntCerts := make(map[string][]*x509.Certificate)
216
217         // Go through every leaf cert ID, and build its valid intermediate certificate list
218         for leafID, intCertList := range allIntCerts {
219                 for _, intCert := range intCertList {
220                         if err := utils.ValidateCertificate(intCert, true); err != nil {
221                                 continue
222                         }
223                         validIntCerts[leafID] = append(validIntCerts[leafID], intCert)
224                 }
225
226         }
227         return validIntCerts
228 }
229
230 // parseAllCerts returns two maps, one with all of the leafCertificates and one
231 // with all the intermediate certificates found in signedRoot
232 func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, map[string][]*x509.Certificate) {
233         if signedRoot == nil {
234                 return nil, nil
235         }
236
237         leafCerts := make(map[string]*x509.Certificate)
238         intCerts := make(map[string][]*x509.Certificate)
239
240         // Before we loop through all root keys available, make sure any exist
241         rootRoles, ok := signedRoot.Signed.Roles[data.CanonicalRootRole]
242         if !ok {
243                 logrus.Debugf("tried to parse certificates from invalid root signed data")
244                 return nil, nil
245         }
246
247         logrus.Debugf("found the following root keys: %v", rootRoles.KeyIDs)
248         // Iterate over every keyID for the root role inside of roots.json
249         for _, keyID := range rootRoles.KeyIDs {
250                 // check that the key exists in the signed root keys map
251                 key, ok := signedRoot.Signed.Keys[keyID]
252                 if !ok {
253                         logrus.Debugf("error while getting data for keyID: %s", keyID)
254                         continue
255                 }
256
257                 // Decode all the x509 certificates that were bundled with this
258                 // Specific root key
259                 decodedCerts, err := utils.LoadCertBundleFromPEM(key.Public())
260                 if err != nil {
261                         logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err)
262                         continue
263                 }
264
265                 // Get all non-CA certificates in the decoded certificates
266                 leafCertList := utils.GetLeafCerts(decodedCerts)
267
268                 // If we got no leaf certificates or we got more than one, fail
269                 if len(leafCertList) != 1 {
270                         logrus.Debugf("invalid chain due to leaf certificate missing or too many leaf certificates for keyID: %s", keyID)
271                         continue
272                 }
273                 // If we found a leaf certificate, assert that the cert bundle started with a leaf
274                 if decodedCerts[0].IsCA {
275                         logrus.Debugf("invalid chain due to leaf certificate not being first certificate for keyID: %s", keyID)
276                         continue
277                 }
278
279                 // Get the ID of the leaf certificate
280                 leafCert := leafCertList[0]
281
282                 // Store the leaf cert in the map
283                 leafCerts[key.ID()] = leafCert
284
285                 // Get all the remainder certificates marked as a CA to be used as intermediates
286                 intermediateCerts := utils.GetIntermediateCerts(decodedCerts)
287                 intCerts[key.ID()] = intermediateCerts
288         }
289
290         return leafCerts, intCerts
291 }