capi/lib/lint/certlint/certlint.go (116 lines of code) (raw):

/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package certlint import ( "bytes" "crypto/x509" "io/ioutil" "log" "os" "os/exec" ) const LIB = `/opt/certlint/lib/:/opt/certlint/ext` const CERTLINT = `/opt/certlint/bin/certlint` const CABLINT = `/opt/certlint/bin/cablint` type Certlint struct { Certlint certlint Cablint certlint } func LintCerts(certificates []*x509.Certificate) ([]Certlint, error) { lints := make([]Certlint, len(certificates)) for i, cert := range certificates { l, err := Lint(cert) if err != nil { return lints, err } lints[i] = l } return lints, nil } func Lint(certificate *x509.Certificate) (Certlint, error) { var result Certlint f, err := ioutil.TempFile("", "certlint") if err != nil { return result, err } defer os.Remove(f.Name()) defer f.Close() err = ioutil.WriteFile(f.Name(), certificate.Raw, 066) if err != nil { return result, err } result.Certlint = lint(f.Name(), CERTLINT) result.Cablint = lint(f.Name(), CABLINT) return result, nil } type certlint struct { Bug []string Info []string Notices []string Warnings []string Errors []string Fatal []string CmdError *string } func NewCertlint() certlint { return certlint{ Bug: make([]string, 0), Info: make([]string, 0), Notices: make([]string, 0), Warnings: make([]string, 0), Errors: make([]string, 0), Fatal: make([]string, 0), CmdError: nil, } } func lint(fname, tool string) certlint { result := NewCertlint() cmd := exec.Command("ruby", "-I", LIB, tool, fname) stdout := bytes.NewBuffer([]byte{}) stderr := bytes.NewBuffer([]byte{}) cmd.Stdout = stdout cmd.Stderr = stderr err := cmd.Run() if err != nil { errStr := err.Error() result.CmdError = &errStr return result } output, err := ioutil.ReadAll(stdout) if err != nil { errStr := err.Error() result.CmdError = &errStr return result } errors, err := ioutil.ReadAll(stderr) if err != nil { errStr := err.Error() result.CmdError = &errStr return result } if string(errors) != "" { errStr := string(errors) result.CmdError = &errStr return result } parseOutput(output, &result) return result } // // B: Bug. Your certificate has a feature not handled by certlint. // I: Information. These are purely informational; no action is needed. // N: Notice. These are items known to cause issues with one or more implementations of certificate processing but are not errors according to the standard. // W: Warning. These are issues where a standard recommends differently but the standard uses terms such as "SHOULD" or "MAY". // E: Error. These are issues where the certificate is not compliant with the standard. // F: Fatal Error. These errors are fatal to the checks and prevent most further checks from being executed. These are extremely bad errors. func parseOutput(output []byte, result *certlint) { for _, line := range bytes.Split(output, []byte{'\n'}) { if bytes.HasPrefix(line, []byte("B: ")) { result.Bug = append(result.Bug, string(line[3:])) } else if bytes.HasPrefix(line, []byte("I: ")) { result.Info = append(result.Info, string(line[3:])) } else if bytes.HasPrefix(line, []byte("N: ")) { result.Notices = append(result.Notices, string(line[3:])) } else if bytes.HasPrefix(line, []byte("W: ")) { result.Warnings = append(result.Warnings, string(line[3:])) } else if bytes.HasPrefix(line, []byte("E: ")) { result.Errors = append(result.Errors, string(line[3:])) } else if bytes.HasPrefix(line, []byte("F: ")) { result.Fatal = append(result.Fatal, string(line[3:])) } else if bytes.Equal(line, []byte("")) { // } else { log.Printf(`unexpected certlint output: "%s"`, string(output)) } } }