1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package validation
18
19import (
20	"bytes"
21	"crypto/x509"
22	"encoding/pem"
23	"fmt"
24	"strings"
25
26	v1 "k8s.io/api/core/v1"
27	apiequality "k8s.io/apimachinery/pkg/api/equality"
28	"k8s.io/apimachinery/pkg/runtime/schema"
29	"k8s.io/apimachinery/pkg/util/diff"
30	"k8s.io/apimachinery/pkg/util/sets"
31	utilvalidation "k8s.io/apimachinery/pkg/util/validation"
32	"k8s.io/apimachinery/pkg/util/validation/field"
33	utilcert "k8s.io/client-go/util/cert"
34	"k8s.io/kubernetes/pkg/apis/certificates"
35	certificatesv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
36	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
37)
38
39var (
40	// trueConditionTypes is the set of condition types which may only have a status of True if present
41	trueConditionTypes = sets.NewString(
42		string(certificates.CertificateApproved),
43		string(certificates.CertificateDenied),
44		string(certificates.CertificateFailed),
45	)
46
47	trueStatusOnly  = sets.NewString(string(v1.ConditionTrue))
48	allStatusValues = sets.NewString(string(v1.ConditionTrue), string(v1.ConditionFalse), string(v1.ConditionUnknown))
49)
50
51type certificateValidationOptions struct {
52	// The following allow modifications only permitted via certain update paths
53
54	// allow populating/modifying Approved/Denied conditions
55	allowSettingApprovalConditions bool
56	// allow populating status.certificate
57	allowSettingCertificate bool
58
59	// allow Approved and Denied conditions to be exist.
60	// we tolerate this when the problem is already present in the persisted object for compatibility.
61	allowBothApprovedAndDenied bool
62
63	// The following are bad things we tolerate for compatibility reasons:
64	// * in requests made via the v1beta1 API
65	// * in update requests where the problem is already present in the persisted object
66
67	// allow modifying status.certificate on an update where the old object has a different certificate
68	allowResettingCertificate bool
69	// allow the legacy-unknown signerName
70	allowLegacySignerName bool
71	// allow conditions with duplicate types
72	allowDuplicateConditionTypes bool
73	// allow conditions with "" types
74	allowEmptyConditionType bool
75	// allow arbitrary content in status.certificate
76	allowArbitraryCertificate bool
77	// allow usages values outside the known set
78	allowUnknownUsages bool
79	// allow duplicate usages values
80	allowDuplicateUsages bool
81}
82
83// validateCSR validates the signature and formatting of a base64-wrapped,
84// PEM-encoded PKCS#10 certificate signing request. If this is invalid, we must
85// not accept the CSR for further processing.
86func validateCSR(obj *certificates.CertificateSigningRequest) error {
87	csr, err := certificates.ParseCSR(obj.Spec.Request)
88	if err != nil {
89		return err
90	}
91	// check that the signature is valid
92	err = csr.CheckSignature()
93	if err != nil {
94		return err
95	}
96	return nil
97}
98
99func validateCertificate(pemData []byte) error {
100	if len(pemData) == 0 {
101		return nil
102	}
103
104	blocks := 0
105	for {
106		block, remainingData := pem.Decode(pemData)
107		if block == nil {
108			break
109		}
110
111		if block.Type != utilcert.CertificateBlockType {
112			return fmt.Errorf("only CERTIFICATE PEM blocks are allowed, found %q", block.Type)
113		}
114		if len(block.Headers) != 0 {
115			return fmt.Errorf("no PEM block headers are permitted")
116		}
117		blocks++
118
119		certs, err := x509.ParseCertificates(block.Bytes)
120		if err != nil {
121			return err
122		}
123		if len(certs) == 0 {
124			return fmt.Errorf("found CERTIFICATE PEM block containing 0 certificates")
125		}
126
127		pemData = remainingData
128	}
129
130	if blocks == 0 {
131		return fmt.Errorf("must contain at least one CERTIFICATE PEM block")
132	}
133
134	return nil
135}
136
137// We don't care what you call your certificate requests.
138func ValidateCertificateRequestName(name string, prefix bool) []string {
139	return nil
140}
141
142func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
143	opts := getValidationOptions(version, csr, nil)
144	return validateCertificateSigningRequest(csr, opts)
145}
146
147var (
148	allValidUsages = sets.NewString(
149		string(certificates.UsageSigning),
150		string(certificates.UsageDigitalSignature),
151		string(certificates.UsageContentCommitment),
152		string(certificates.UsageKeyEncipherment),
153		string(certificates.UsageKeyAgreement),
154		string(certificates.UsageDataEncipherment),
155		string(certificates.UsageCertSign),
156		string(certificates.UsageCRLSign),
157		string(certificates.UsageEncipherOnly),
158		string(certificates.UsageDecipherOnly),
159		string(certificates.UsageAny),
160		string(certificates.UsageServerAuth),
161		string(certificates.UsageClientAuth),
162		string(certificates.UsageCodeSigning),
163		string(certificates.UsageEmailProtection),
164		string(certificates.UsageSMIME),
165		string(certificates.UsageIPsecEndSystem),
166		string(certificates.UsageIPsecTunnel),
167		string(certificates.UsageIPsecUser),
168		string(certificates.UsageTimestamping),
169		string(certificates.UsageOCSPSigning),
170		string(certificates.UsageMicrosoftSGC),
171		string(certificates.UsageNetscapeSGC),
172	)
173)
174
175func validateCertificateSigningRequest(csr *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList {
176	isNamespaced := false
177	allErrs := apivalidation.ValidateObjectMeta(&csr.ObjectMeta, isNamespaced, ValidateCertificateRequestName, field.NewPath("metadata"))
178
179	specPath := field.NewPath("spec")
180	err := validateCSR(csr)
181	if err != nil {
182		allErrs = append(allErrs, field.Invalid(specPath.Child("request"), csr.Spec.Request, fmt.Sprintf("%v", err)))
183	}
184	if len(csr.Spec.Usages) == 0 {
185		allErrs = append(allErrs, field.Required(specPath.Child("usages"), "usages must be provided"))
186	}
187	if !opts.allowUnknownUsages {
188		for i, usage := range csr.Spec.Usages {
189			if !allValidUsages.Has(string(usage)) {
190				allErrs = append(allErrs, field.NotSupported(specPath.Child("usages").Index(i), usage, allValidUsages.List()))
191			}
192		}
193	}
194	if !opts.allowDuplicateUsages {
195		seen := make(map[certificates.KeyUsage]bool, len(csr.Spec.Usages))
196		for i, usage := range csr.Spec.Usages {
197			if seen[usage] {
198				allErrs = append(allErrs, field.Duplicate(specPath.Child("usages").Index(i), usage))
199			}
200			seen[usage] = true
201		}
202	}
203	if !opts.allowLegacySignerName && csr.Spec.SignerName == certificates.LegacyUnknownSignerName {
204		allErrs = append(allErrs, field.Invalid(specPath.Child("signerName"), csr.Spec.SignerName, "the legacy signerName is not allowed via this API version"))
205	} else {
206		allErrs = append(allErrs, ValidateCertificateSigningRequestSignerName(specPath.Child("signerName"), csr.Spec.SignerName)...)
207	}
208	if csr.Spec.ExpirationSeconds != nil && *csr.Spec.ExpirationSeconds < 600 {
209		allErrs = append(allErrs, field.Invalid(specPath.Child("expirationSeconds"), *csr.Spec.ExpirationSeconds, "may not specify a duration less than 600 seconds (10 minutes)"))
210	}
211	allErrs = append(allErrs, validateConditions(field.NewPath("status", "conditions"), csr, opts)...)
212
213	if !opts.allowArbitraryCertificate {
214		if err := validateCertificate(csr.Status.Certificate); err != nil {
215			allErrs = append(allErrs, field.Invalid(field.NewPath("status", "certificate"), "<certificate data>", err.Error()))
216		}
217	}
218
219	return allErrs
220}
221
222func validateConditions(fldPath *field.Path, csr *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList {
223	allErrs := field.ErrorList{}
224
225	seenTypes := map[certificates.RequestConditionType]bool{}
226	hasApproved := false
227	hasDenied := false
228
229	for i, c := range csr.Status.Conditions {
230
231		if !opts.allowEmptyConditionType {
232			if len(c.Type) == 0 {
233				allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("type"), ""))
234			}
235		}
236
237		allowedStatusValues := allStatusValues
238		if trueConditionTypes.Has(string(c.Type)) {
239			allowedStatusValues = trueStatusOnly
240		}
241		switch {
242		case c.Status == "":
243			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("status"), ""))
244		case !allowedStatusValues.Has(string(c.Status)):
245			allErrs = append(allErrs, field.NotSupported(fldPath.Index(i).Child("status"), c.Status, allowedStatusValues.List()))
246		}
247
248		if !opts.allowBothApprovedAndDenied {
249			switch c.Type {
250			case certificates.CertificateApproved:
251				hasApproved = true
252				if hasDenied {
253					allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), c.Type, "Approved and Denied conditions are mutually exclusive"))
254				}
255			case certificates.CertificateDenied:
256				hasDenied = true
257				if hasApproved {
258					allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), c.Type, "Approved and Denied conditions are mutually exclusive"))
259				}
260			}
261		}
262
263		if !opts.allowDuplicateConditionTypes {
264			if seenTypes[c.Type] {
265				allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("type"), c.Type))
266			}
267			seenTypes[c.Type] = true
268		}
269	}
270
271	return allErrs
272}
273
274// ensure signerName is of the form domain.com/something and up to 571 characters.
275// This length and format is specified to accommodate signerNames like:
276// <fqdn>/<resource-namespace>.<resource-name>.
277// The max length of a FQDN is 253 characters (DNS1123Subdomain max length)
278// The max length of a namespace name is 63 characters (DNS1123Label max length)
279// The max length of a resource name is 253 characters (DNS1123Subdomain max length)
280// We then add an additional 2 characters to account for the one '.' and one '/'.
281func ValidateCertificateSigningRequestSignerName(fldPath *field.Path, signerName string) field.ErrorList {
282	var el field.ErrorList
283	if len(signerName) == 0 {
284		el = append(el, field.Required(fldPath, "signerName must be provided"))
285		return el
286	}
287
288	segments := strings.Split(signerName, "/")
289	// validate that there is one '/' in the signerName.
290	// we do this after validating the domain segment to provide more info to the user.
291	if len(segments) != 2 {
292		el = append(el, field.Invalid(fldPath, signerName, "must be a fully qualified domain and path of the form 'example.com/signer-name'"))
293		// return early here as we should not continue attempting to validate a missing or malformed path segment
294		// (i.e. one containing multiple or zero `/`)
295		return el
296	}
297
298	// validate that segments[0] is less than 253 characters altogether
299	maxDomainSegmentLength := utilvalidation.DNS1123SubdomainMaxLength
300	if len(segments[0]) > maxDomainSegmentLength {
301		el = append(el, field.TooLong(fldPath, segments[0], maxDomainSegmentLength))
302	}
303	// validate that segments[0] consists of valid DNS1123 labels separated by '.'
304	domainLabels := strings.Split(segments[0], ".")
305	for _, lbl := range domainLabels {
306		// use IsDNS1123Label as we want to ensure the max length of any single label in the domain
307		// is 63 characters
308		if errs := utilvalidation.IsDNS1123Label(lbl); len(errs) > 0 {
309			for _, err := range errs {
310				el = append(el, field.Invalid(fldPath, segments[0], fmt.Sprintf("validating label %q: %s", lbl, err)))
311			}
312			// if we encounter any errors whilst parsing the domain segment, break from
313			// validation as any further error messages will be duplicates, and non-distinguishable
314			// from each other, confusing users.
315			break
316		}
317	}
318
319	// validate that there is at least one '.' in segments[0]
320	if len(domainLabels) < 2 {
321		el = append(el, field.Invalid(fldPath, segments[0], "should be a domain with at least two segments separated by dots"))
322	}
323
324	// validate that segments[1] consists of valid DNS1123 subdomains separated by '.'.
325	pathLabels := strings.Split(segments[1], ".")
326	for _, lbl := range pathLabels {
327		// use IsDNS1123Subdomain because it enforces a length restriction of 253 characters
328		// which is required in order to fit a full resource name into a single 'label'
329		if errs := utilvalidation.IsDNS1123Subdomain(lbl); len(errs) > 0 {
330			for _, err := range errs {
331				el = append(el, field.Invalid(fldPath, segments[1], fmt.Sprintf("validating label %q: %s", lbl, err)))
332			}
333			// if we encounter any errors whilst parsing the path segment, break from
334			// validation as any further error messages will be duplicates, and non-distinguishable
335			// from each other, confusing users.
336			break
337		}
338	}
339
340	// ensure that segments[1] can accommodate a dns label + dns subdomain + '.'
341	maxPathSegmentLength := utilvalidation.DNS1123SubdomainMaxLength + utilvalidation.DNS1123LabelMaxLength + 1
342	maxSignerNameLength := maxDomainSegmentLength + maxPathSegmentLength + 1
343	if len(signerName) > maxSignerNameLength {
344		el = append(el, field.TooLong(fldPath, signerName, maxSignerNameLength))
345	}
346
347	return el
348}
349
350func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
351	opts := getValidationOptions(version, newCSR, oldCSR)
352	return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
353}
354
355func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
356	opts := getValidationOptions(version, newCSR, oldCSR)
357	opts.allowSettingCertificate = true
358	return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
359}
360
361func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
362	opts := getValidationOptions(version, newCSR, oldCSR)
363	opts.allowSettingApprovalConditions = true
364	return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
365}
366
367func validateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList {
368	validationErrorList := validateCertificateSigningRequest(newCSR, opts)
369	metaUpdateErrorList := apivalidation.ValidateObjectMetaUpdate(&newCSR.ObjectMeta, &oldCSR.ObjectMeta, field.NewPath("metadata"))
370
371	// prevent removal of existing Approved/Denied/Failed conditions
372	for _, t := range []certificates.RequestConditionType{certificates.CertificateApproved, certificates.CertificateDenied, certificates.CertificateFailed} {
373		oldConditions := findConditions(oldCSR, t)
374		newConditions := findConditions(newCSR, t)
375		if len(newConditions) < len(oldConditions) {
376			validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not remove a condition of type %q", t)))
377		}
378	}
379
380	if !opts.allowSettingApprovalConditions {
381		// prevent addition/removal/modification of Approved/Denied conditions
382		for _, t := range []certificates.RequestConditionType{certificates.CertificateApproved, certificates.CertificateDenied} {
383			oldConditions := findConditions(oldCSR, t)
384			newConditions := findConditions(newCSR, t)
385			switch {
386			case len(newConditions) < len(oldConditions):
387				// removals are prevented above
388			case len(newConditions) > len(oldConditions):
389				validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not add a condition of type %q", t)))
390			case !apiequality.Semantic.DeepEqual(oldConditions, newConditions):
391				conditionDiff := diff.ObjectDiff(oldConditions, newConditions)
392				validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not modify a condition of type %q\n%v", t, conditionDiff)))
393			}
394		}
395	}
396
397	if !bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate) {
398		if !opts.allowSettingCertificate {
399			validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "certificate"), "updates may not set certificate content"))
400		} else if !opts.allowResettingCertificate && len(oldCSR.Status.Certificate) > 0 {
401			validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "certificate"), "updates may not modify existing certificate content"))
402		}
403	}
404
405	return append(validationErrorList, metaUpdateErrorList...)
406}
407
408// findConditions returns all instances of conditions of the specified type
409func findConditions(csr *certificates.CertificateSigningRequest, conditionType certificates.RequestConditionType) []certificates.CertificateSigningRequestCondition {
410	var retval []certificates.CertificateSigningRequestCondition
411	for i, c := range csr.Status.Conditions {
412		if c.Type == conditionType {
413			retval = append(retval, csr.Status.Conditions[i])
414		}
415	}
416	return retval
417}
418
419// getValidationOptions returns the validation options to be
420// compatible with the specified version and existing CSR.
421// oldCSR may be nil if this is a create request.
422// validation options related to subresource-specific capabilities are set to false.
423func getValidationOptions(version schema.GroupVersion, newCSR, oldCSR *certificates.CertificateSigningRequest) certificateValidationOptions {
424	return certificateValidationOptions{
425		allowResettingCertificate:    allowResettingCertificate(version),
426		allowBothApprovedAndDenied:   allowBothApprovedAndDenied(oldCSR),
427		allowLegacySignerName:        allowLegacySignerName(version, oldCSR),
428		allowDuplicateConditionTypes: allowDuplicateConditionTypes(version, oldCSR),
429		allowEmptyConditionType:      allowEmptyConditionType(version, oldCSR),
430		allowArbitraryCertificate:    allowArbitraryCertificate(version, newCSR, oldCSR),
431		allowDuplicateUsages:         allowDuplicateUsages(version, oldCSR),
432		allowUnknownUsages:           allowUnknownUsages(version, oldCSR),
433	}
434}
435
436func allowResettingCertificate(version schema.GroupVersion) bool {
437	// compatibility with v1beta1
438	return version == certificatesv1beta1.SchemeGroupVersion
439}
440
441func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest) bool {
442	if oldCSR == nil {
443		return false
444	}
445	approved := false
446	denied := false
447	for _, c := range oldCSR.Status.Conditions {
448		if c.Type == certificates.CertificateApproved {
449			approved = true
450		} else if c.Type == certificates.CertificateDenied {
451			denied = true
452		}
453	}
454	// compatibility with existing data
455	return approved && denied
456}
457
458func allowLegacySignerName(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
459	switch {
460	case version == certificatesv1beta1.SchemeGroupVersion:
461		return true // compatibility with v1beta1
462	case oldCSR != nil && oldCSR.Spec.SignerName == certificates.LegacyUnknownSignerName:
463		return true // compatibility with existing data
464	default:
465		return false
466	}
467}
468
469func allowDuplicateConditionTypes(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
470	switch {
471	case version == certificatesv1beta1.SchemeGroupVersion:
472		return true // compatibility with v1beta1
473	case oldCSR != nil && hasDuplicateConditionTypes(oldCSR):
474		return true // compatibility with existing data
475	default:
476		return false
477	}
478}
479func hasDuplicateConditionTypes(csr *certificates.CertificateSigningRequest) bool {
480	seen := map[certificates.RequestConditionType]bool{}
481	for _, c := range csr.Status.Conditions {
482		if seen[c.Type] {
483			return true
484		}
485		seen[c.Type] = true
486	}
487	return false
488}
489
490func allowEmptyConditionType(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
491	switch {
492	case version == certificatesv1beta1.SchemeGroupVersion:
493		return true // compatibility with v1beta1
494	case oldCSR != nil && hasEmptyConditionType(oldCSR):
495		return true // compatibility with existing data
496	default:
497		return false
498	}
499}
500func hasEmptyConditionType(csr *certificates.CertificateSigningRequest) bool {
501	for _, c := range csr.Status.Conditions {
502		if len(c.Type) == 0 {
503			return true
504		}
505	}
506	return false
507}
508
509func allowArbitraryCertificate(version schema.GroupVersion, newCSR, oldCSR *certificates.CertificateSigningRequest) bool {
510	switch {
511	case version == certificatesv1beta1.SchemeGroupVersion:
512		return true // compatibility with v1beta1
513	case newCSR != nil && oldCSR != nil && bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate):
514		return true // tolerate updates that don't touch status.certificate
515	case oldCSR != nil && validateCertificate(oldCSR.Status.Certificate) != nil:
516		return true // compatibility with existing data
517	default:
518		return false
519	}
520}
521
522func allowUnknownUsages(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
523	switch {
524	case version == certificatesv1beta1.SchemeGroupVersion:
525		return true // compatibility with v1beta1
526	case oldCSR != nil && hasUnknownUsage(oldCSR.Spec.Usages):
527		return true // compatibility with existing data
528	default:
529		return false
530	}
531}
532
533func hasUnknownUsage(usages []certificates.KeyUsage) bool {
534	for _, usage := range usages {
535		if !allValidUsages.Has(string(usage)) {
536			return true
537		}
538	}
539	return false
540}
541
542func allowDuplicateUsages(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
543	switch {
544	case version == certificatesv1beta1.SchemeGroupVersion:
545		return true // compatibility with v1beta1
546	case oldCSR != nil && hasDuplicateUsage(oldCSR.Spec.Usages):
547		return true // compatibility with existing data
548	default:
549		return false
550	}
551}
552
553func hasDuplicateUsage(usages []certificates.KeyUsage) bool {
554	seen := make(map[certificates.KeyUsage]bool, len(usages))
555	for _, usage := range usages {
556		if seen[usage] {
557			return true
558		}
559		seen[usage] = true
560	}
561	return false
562}
563