Add lint to check for a reserved policy identifier in S/MIME certificates#1011
Conversation
| lint.RegisterCertificateLint(&lint.CertificateLint{ | ||
| LintMetadata: lint.LintMetadata{ | ||
| Name: "e_cabf_policy_missing", | ||
| Description: "The subscriber cert SHALL include one of the reserved policy OIDs in §7.1.6.1", |
There was a problem hiding this comment.
I admit that I'm a bit confused.
First of all, unless my eyes are crossed, I believe that the requirement is that subscriber certs MUST NOT contain any of.
2.23.140.1.2.12.23.140.1.2.22.23.140.1.2.32.23.140.1.1
Additionally, util.IsSMIMEBRCertificate checks for the presence of at least one of these OIDs.
2.23.140.1.5.1.12.23.140.1.5.1.22.23.140.1.5.1.32.23.140.1.5.2.12.23.140.1.5.2.22.23.140.1.5.2.32.23.140.1.5.3.12.23.140.1.5.3.22.23.140.1.5.3.32.23.140.1.5.4.12.23.140.1.5.4.22.23.140.1.5.4.3
So unless I am misreading the BRs then I do not believe that we are checking the correct OIDs here.
|
@christopher-henderson , I think you may be wrongly assuming that this lint is for TLS certificates, but it is not: it's for S/MIME certificates. Besides, have you tried ZLinting an S/MIME certificates that lacks a CABF S/MIME BRs reserved policy OID? If you do, you'll find that Zlint has nothing to complain, which is weird. Please have a look at the S/MIME BRs, section 7.1.2.3. (btw: happy new year!) |
|
@defacto64 I face palm every time I spend an hour double checking everything, from the publication to the ZLint implementation, just to find that I'm reading the entirely wrong document 😮💨
Since CABF wants a cert to func ContainsExactlyOneSMIMEPolicy(policies []asn1.ObjectIdentifier) bool {
found := 0
smimePolicies := map[string]bool{
SMIMEBRMailboxValidatedLegacyOID.String(): true,
SMIMEBRMailboxValidatedMultipurposeOID.String(): true,
SMIMEBRMailboxValidatedStrictOID.String(): true,
SMIMEBROrganizationValidatedLegacyOID.String(): true,
SMIMEBROrganizationValidatedMultipurposeOID.String(): true,
SMIMEBROrganizationValidatedStrictOID.String(): true,
SMIMEBRSponsorValidatedLegacyOID.String(): true,
SMIMEBRSponsorValidatedMultipurposeOID.String(): true,
SMIMEBRSponsorValidatedStrictOID.String(): true,
SMIMEBRIndividualValidatedLegacyOID.String(): true,
SMIMEBRIndividualValidatedMultipurposeOID.String(): true,
SMIMEBRIndividualValidatedStrictOID.String(): true,
}
for _, oid := range policies {
if _, present := smimePolicies[oid.String()]; present {
found++
}
}
return found == 1
}Here's a decent enough test suite for this function. func TestExactlyOneSMIMEPolicy(t *testing.T) {
tests := []struct {
name string
policies []asn1.ObjectIdentifier
want bool
}{
{
name: "empty slice returns false",
policies: []asn1.ObjectIdentifier{},
want: false,
},
{
name: "nil slice returns false",
policies: nil,
want: false,
},
{
name: "single mailbox validated legacy policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedLegacyOID,
},
want: true,
},
{
name: "single mailbox validated multipurpose policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedMultipurposeOID,
},
want: true,
},
{
name: "single mailbox validated strict policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedStrictOID,
},
want: true,
},
{
name: "single organization validated legacy policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBROrganizationValidatedLegacyOID,
},
want: true,
},
{
name: "single organization validated multipurpose policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBROrganizationValidatedMultipurposeOID,
},
want: true,
},
{
name: "single organization validated strict policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBROrganizationValidatedStrictOID,
},
want: true,
},
{
name: "single sponsor validated legacy policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRSponsorValidatedLegacyOID,
},
want: true,
},
{
name: "single sponsor validated multipurpose policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRSponsorValidatedMultipurposeOID,
},
want: true,
},
{
name: "single sponsor validated strict policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRSponsorValidatedStrictOID,
},
want: true,
},
{
name: "single individual validated legacy policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRIndividualValidatedLegacyOID,
},
want: true,
},
{
name: "single individual validated multipurpose policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRIndividualValidatedMultipurposeOID,
},
want: true,
},
{
name: "single individual validated strict policy returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRIndividualValidatedStrictOID,
},
want: true,
},
{
name: "two different SMIME policies returns false",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedLegacyOID,
SMIMEBROrganizationValidatedLegacyOID,
},
want: false,
},
{
name: "two same SMIME policies returns false",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedLegacyOID,
SMIMEBRMailboxValidatedLegacyOID,
},
want: false,
},
{
name: "three SMIME policies returns false",
policies: []asn1.ObjectIdentifier{
SMIMEBRMailboxValidatedLegacyOID,
SMIMEBROrganizationValidatedMultipurposeOID,
SMIMEBRIndividualValidatedStrictOID,
},
want: false,
},
{
name: "one SMIME policy with non-SMIME policies returns true",
policies: []asn1.ObjectIdentifier{
BRDomainValidatedOID,
SMIMEBRMailboxValidatedLegacyOID,
AnyPolicyOID,
},
want: true,
},
{
name: "two SMIME policies with non-SMIME policies returns false",
policies: []asn1.ObjectIdentifier{
BRDomainValidatedOID,
SMIMEBRMailboxValidatedLegacyOID,
SMIMEBROrganizationValidatedMultipurposeOID,
AnyPolicyOID,
},
want: false,
},
{
name: "only non-SMIME policies returns false",
policies: []asn1.ObjectIdentifier{
BRDomainValidatedOID,
BROrganizationValidatedOID,
BRExtendedValidatedOID,
},
want: false,
},
{
name: "SMIME policy at end of list returns true",
policies: []asn1.ObjectIdentifier{
BRDomainValidatedOID,
BROrganizationValidatedOID,
SMIMEBRIndividualValidatedStrictOID,
},
want: true,
},
{
name: "SMIME policy at beginning of list returns true",
policies: []asn1.ObjectIdentifier{
SMIMEBRSponsorValidatedMultipurposeOID,
BRDomainValidatedOID,
BROrganizationValidatedOID,
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ContainsExactlyOneSMIMEPolicy(tt.policies)
if got != tt.want {
t.Errorf("ContainsExactlyOneSMIMEPolicy() = %v, want %v", got, tt.want)
}
})
}
} |
|
Yes, it was already clear to me that the CABF S/MIME requirement is actually twofold: 1) there must be a Reserved Policy OID, but 2) there must not be more than one. However, it seemed OK to me to have one lint that handles point 1 and a second lint (to be implemented) for point 2. Frankly, I'm totally fine with a single lint that enforces both points, but sometimes I get the impression that you don't much like lints that perform multiple checks at once, while other times it seems like you actually like them :) |
Well in my own mental model this is not checking two things.
This is why I wrote the function to accept a list of OIDs rather than certificates, it is much easier to unit test. I would be fine leaning on the unit test provided and keeping the current certs for a smoke check. |
|
So, first I added your |
|
Thank you for another valuable lint @defacto64! |

I've noticed that ZLint doesn't raise any errors when linting an S/MIME certificate which lacks a reserved policy identifier among those required by the S/MIME BRs (see section 7.1.2.3), which seems counterintuitive to me. I am therefore proposing this trivial lint to resolve the issue.
(It could/should also be verified that there is only one such policy OID in the certificate, but I'll leave that to some other volunteer!)