1
1
package httputil
2
2
3
3
import (
4
- "bytes"
5
4
"crypto/x509"
6
- "encoding/base64"
7
- "encoding/pem"
8
5
"fmt"
9
6
"os"
10
7
"path/filepath"
@@ -13,192 +10,6 @@ import (
13
10
"github.com/go-logr/logr"
14
11
)
15
12
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
-
202
13
func NewCertPool (caDir string , log logr.Logger ) (* x509.CertPool , error ) {
203
14
caCertPool , err := x509 .SystemCertPool ()
204
15
if err != nil {
@@ -231,11 +42,10 @@ func NewCertPool(caDir string, log logr.Logger) (*x509.CertPool, error) {
231
42
if err != nil {
232
43
return nil , fmt .Errorf ("error reading cert file %q: %w" , file , err )
233
44
}
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 ++
237
48
}
238
- count ++
239
49
}
240
50
241
51
// Found no certs!
0 commit comments