ssh: make the public key cache a 1-entry FIFO cache · golang/crypto@b4f1988 (original) (raw)
2 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -149,15 +149,21 @@ func (s *ServerConfig) AddHostKey(key Signer) { | ||
149 | 149 | } |
150 | 150 | |
151 | 151 | // cachedPubKey contains the results of querying whether a public key is |
152 | -// acceptable for a user. | |
152 | +// acceptable for a user. This is a FIFO cache. | |
153 | 153 | type cachedPubKey struct { |
154 | 154 | user string |
155 | 155 | pubKeyData []byte |
156 | 156 | result error |
157 | 157 | perms *Permissions |
158 | 158 | } |
159 | 159 | |
160 | -const maxCachedPubKeys = 16 | |
160 | +// maxCachedPubKeys is the number of cache entries we store. | |
161 | +// | |
162 | +// Due to consistent misuse of the PublicKeyCallback API, we have reduced this | |
163 | +// to 1, such that the only key in the cache is the most recently seen one. This | |
164 | +// forces the behavior that the last call to PublicKeyCallback will always be | |
165 | +// with the key that is used for authentication. | |
166 | +const maxCachedPubKeys = 1 | |
161 | 167 | |
162 | 168 | // pubKeyCache caches tests for public keys. Since SSH clients |
163 | 169 | // will query whether a public key is acceptable before attempting to |
@@ -179,9 +185,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { | ||
179 | 185 | |
180 | 186 | // add adds the given tuple to the cache. |
181 | 187 | func (c *pubKeyCache) add(candidate cachedPubKey) { |
182 | -if len(c.keys) < maxCachedPubKeys { | |
183 | -c.keys = append(c.keys, candidate) | |
188 | +if len(c.keys) >= maxCachedPubKeys { | |
189 | +c.keys = c.keys[1:] | |
184 | 190 | } |
191 | +c.keys = append(c.keys, candidate) | |
185 | 192 | } |
186 | 193 | |
187 | 194 | // ServerConn is an authenticated SSH connection, as seen from the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -5,6 +5,7 @@ | ||
5 | 5 | package ssh |
6 | 6 | |
7 | 7 | import ( |
8 | +"bytes" | |
8 | 9 | "errors" |
9 | 10 | "fmt" |
10 | 11 | "io" |
@@ -299,6 +300,54 @@ func TestBannerError(t *testing.T) { | ||
299 | 300 | } |
300 | 301 | } |
301 | 302 | |
303 | +func TestPublicKeyCallbackLastSeen(t *testing.T) { | |
304 | +var lastSeenKey PublicKey | |
305 | + | |
306 | +c1, c2, err := netPipe() | |
307 | +if err != nil { | |
308 | +t.Fatalf("netPipe: %v", err) | |
309 | + } | |
310 | +defer c1.Close() | |
311 | +defer c2.Close() | |
312 | +serverConf := &ServerConfig{ | |
313 | +PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { | |
314 | +lastSeenKey = key | |
315 | +fmt.Printf("seen %#v\n", key) | |
316 | +if _, ok := key.(*dsaPublicKey); !ok { | |
317 | +return nil, errors.New("nope") | |
318 | + } | |
319 | +return nil, nil | |
320 | + }, | |
321 | + } | |
322 | +serverConf.AddHostKey(testSigners["ecdsap256"]) | |
323 | + | |
324 | +done := make(chan struct{}) | |
325 | +go func() { | |
326 | +defer close(done) | |
327 | +NewServerConn(c1, serverConf) | |
328 | + }() | |
329 | + | |
330 | +clientConf := ClientConfig{ | |
331 | +User: "user", | |
332 | +Auth: []AuthMethod{ | |
333 | +PublicKeys(testSigners["rsa"], testSigners["dsa"], testSigners["ed25519"]), | |
334 | + }, | |
335 | +HostKeyCallback: InsecureIgnoreHostKey(), | |
336 | + } | |
337 | + | |
338 | +_, _, _, err = NewClientConn(c2, "", &clientConf) | |
339 | +if err != nil { | |
340 | +t.Fatal(err) | |
341 | + } | |
342 | +<-done | |
343 | + | |
344 | +expectedPublicKey := testSigners["dsa"].PublicKey().Marshal() | |
345 | +lastSeenMarshalled := lastSeenKey.Marshal() | |
346 | +if !bytes.Equal(lastSeenMarshalled, expectedPublicKey) { | |
347 | +t.Errorf("unexpected key: got %#v, want %#v", lastSeenKey, testSigners["dsa"].PublicKey()) | |
348 | + } | |
349 | +} | |
350 | + | |
302 | 351 | type markerConn struct { |
303 | 352 | closed uint32 |
304 | 353 | used uint32 |