11package httputil
22
33import (
4- "bytes"
54 "crypto/x509"
6- "encoding/base64"
7- "encoding/pem"
85 "fmt"
96 "os"
107 "path/filepath"
@@ -13,192 +10,6 @@ import (
1310 "github.com/go-logr/logr"
1411)
1512
16- var pemStart = []byte ("\n -----BEGIN " )
17- var pemEnd = []byte ("\n -----END " )
18- var pemEndOfLine = []byte ("-----" )
19- var colon = []byte (":" )
20-
21- // getLine results the first \r\n or \n delineated line from the given byte
22- // array. The line does not include trailing whitespace or the trailing new
23- // line bytes. The remainder of the byte array (also not including the new line
24- // bytes) is also returned and this will always be smaller than the original
25- // argument.
26- func getLine (data []byte ) ([]byte , []byte ) {
27- i := bytes .IndexByte (data , '\n' )
28- var j int
29- if i < 0 {
30- i = len (data )
31- j = i
32- } else {
33- j = i + 1
34- if i > 0 && data [i - 1 ] == '\r' {
35- i --
36- }
37- }
38- return bytes .TrimRight (data [0 :i ], " \t " ), data [j :]
39- }
40-
41- // removeSpacesAndTabs returns a copy of its input with all spaces and tabs
42- // removed, if there were any. Otherwise, the input is returned unchanged.
43- //
44- // The base64 decoder already skips newline characters, so we don't need to
45- // filter them out here.
46- func removeSpacesAndTabs (data []byte ) []byte {
47- if ! bytes .ContainsAny (data , " \t " ) {
48- // Fast path; most base64 data within PEM contains newlines, but
49- // no spaces nor tabs. Skip the extra alloc and work.
50- return data
51- }
52- result := make ([]byte , len (data ))
53- n := 0
54-
55- for _ , b := range data {
56- if b == ' ' || b == '\t' {
57- continue
58- }
59- result [n ] = b
60- n ++
61- }
62-
63- return result [0 :n ]
64- }
65-
66- // This version of pem.Decode() is a bit less flexible, it will not skip over bad PEM
67- // It is basically the guts of pem.Decode() inside the outer for loop, with error
68- // returns rather than continues
69- func pemDecode (data []byte ) (* pem.Block , []byte ) {
70- // pemStart begins with a newline. However, at the very beginning of
71- // the byte array, we'll accept the start string without it.
72- rest := data
73- if bytes .HasPrefix (rest , pemStart [1 :]) {
74- rest = rest [len (pemStart )- 1 :]
75- } else if _ , after , ok := bytes .Cut (rest , pemStart ); ok {
76- rest = after
77- } else {
78- return nil , data
79- }
80-
81- var typeLine []byte
82- typeLine , rest = getLine (rest )
83- if ! bytes .HasSuffix (typeLine , pemEndOfLine ) {
84- return nil , data
85- }
86- typeLine = typeLine [0 : len (typeLine )- len (pemEndOfLine )]
87-
88- p := & pem.Block {
89- Headers : make (map [string ]string ),
90- Type : string (typeLine ),
91- }
92-
93- for {
94- // This loop terminates because getLine's second result is
95- // always smaller than its argument.
96- if len (rest ) == 0 {
97- return nil , data
98- }
99- line , next := getLine (rest )
100-
101- key , val , ok := bytes .Cut (line , colon )
102- if ! ok {
103- break
104- }
105-
106- key = bytes .TrimSpace (key )
107- val = bytes .TrimSpace (val )
108- p .Headers [string (key )] = string (val )
109- rest = next
110- }
111-
112- var endIndex , endTrailerIndex int
113-
114- // If there were no headers, the END line might occur
115- // immediately, without a leading newline.
116- if len (p .Headers ) == 0 && bytes .HasPrefix (rest , pemEnd [1 :]) {
117- endIndex = 0
118- endTrailerIndex = len (pemEnd ) - 1
119- } else {
120- endIndex = bytes .Index (rest , pemEnd )
121- endTrailerIndex = endIndex + len (pemEnd )
122- }
123-
124- if endIndex < 0 {
125- return nil , data
126- }
127-
128- // After the "-----" of the ending line, there should be the same type
129- // and then a final five dashes.
130- endTrailer := rest [endTrailerIndex :]
131- endTrailerLen := len (typeLine ) + len (pemEndOfLine )
132- if len (endTrailer ) < endTrailerLen {
133- return nil , data
134- }
135-
136- restOfEndLine := endTrailer [endTrailerLen :]
137- endTrailer = endTrailer [:endTrailerLen ]
138- if ! bytes .HasPrefix (endTrailer , typeLine ) ||
139- ! bytes .HasSuffix (endTrailer , pemEndOfLine ) {
140- return nil , data
141- }
142-
143- // The line must end with only whitespace.
144- if s , _ := getLine (restOfEndLine ); len (s ) != 0 {
145- return nil , data
146- }
147-
148- base64Data := removeSpacesAndTabs (rest [:endIndex ])
149- p .Bytes = make ([]byte , base64 .StdEncoding .DecodedLen (len (base64Data )))
150- n , err := base64 .StdEncoding .Decode (p .Bytes , base64Data )
151- if err != nil {
152- return nil , data
153- }
154- p .Bytes = p .Bytes [:n ]
155-
156- // the -1 is because we might have only matched pemEnd without the
157- // leading newline if the PEM block was empty.
158- _ , rest = getLine (rest [endIndex + len (pemEnd )- 1 :])
159- return p , rest
160- }
161-
162- // This version of (*x509.CertPool).AppendCertsFromPEM() will error out if parsing fails
163- func appendCertsFromPEM (s * x509.CertPool , pemCerts []byte , firstExpiration * time.Time ) error {
164- n := 1
165- for len (pemCerts ) > 0 {
166- var block * pem.Block
167- block , pemCerts = pemDecode (pemCerts )
168- if block == nil {
169- return fmt .Errorf ("unable to PEM decode cert %d" , n )
170- }
171- // ignore non-certificates (e.g. keys)
172- if block .Type != "CERTIFICATE" {
173- continue
174- }
175- if len (block .Headers ) != 0 {
176- // This is a cert, but we're ignoring it, so bump the counter
177- n ++
178- continue
179- }
180-
181- cert , err := x509 .ParseCertificate (block .Bytes )
182- if err != nil {
183- return fmt .Errorf ("unable to parse cert %d: %w" , n , err )
184- }
185- if firstExpiration .IsZero () || firstExpiration .After (cert .NotAfter ) {
186- * firstExpiration = cert .NotAfter
187- }
188- now := time .Now ()
189- if now .Before (cert .NotBefore ) {
190- return fmt .Errorf ("not yet valid cert %d: %q" , n , cert .NotBefore .Format (time .RFC3339 ))
191- } else if now .After (cert .NotAfter ) {
192- return fmt .Errorf ("expired cert %d: %q" , n , cert .NotAfter .Format (time .RFC3339 ))
193- }
194- // no return values - panics or always succeeds
195- s .AddCert (cert )
196- n ++
197- }
198-
199- return nil
200- }
201-
20213func NewCertPool (caDir string , log logr.Logger ) (* x509.CertPool , error ) {
20314 caCertPool , err := x509 .SystemCertPool ()
20415 if err != nil {
@@ -231,11 +42,10 @@ func NewCertPool(caDir string, log logr.Logger) (*x509.CertPool, error) {
23142 if err != nil {
23243 return nil , fmt .Errorf ("error reading cert file %q: %w" , file , err )
23344 }
234- err = appendCertsFromPEM ( caCertPool , data , & firstExpiration )
235- if err != nil {
236- return nil , fmt . Errorf ( "error adding cert file %q: %w" , file , err )
45+ // The return indicates if any certs were added
46+ if caCertPool . AppendCertsFromPEM ( data ) {
47+ count ++
23748 }
238- count ++
23949 }
24050
24151 // Found no certs!
0 commit comments