This is a workaround to only process one identity file at a time, even if they have passphrases. It must be combined with retryable authentication to work properly Despite returning an array of signers, we only ever provide one since it allows proper user interaction in between attempts A signific
(connCtx context.Context, sshKeywords *wconfig.ConnKeywords, authSockSignersExt []ssh.Signer, agentClient agent.ExtendedAgent, debugInfo *ConnectionDebugInfo)
| 274 | // keys from being attempted. But if there's an error because of a dummy |
| 275 | // file, the library can still try again with a new key. |
| 276 | func createPublicKeyCallback(connCtx context.Context, sshKeywords *wconfig.ConnKeywords, authSockSignersExt []ssh.Signer, agentClient agent.ExtendedAgent, debugInfo *ConnectionDebugInfo) func() ([]ssh.Signer, error) { |
| 277 | var identityFiles []string |
| 278 | existingKeys := make(map[string][]byte) |
| 279 | |
| 280 | // checking the file early prevents us from needing to send a |
| 281 | // dummy signer if there's a problem with the signer |
| 282 | for _, identityFile := range sshKeywords.SshIdentityFile { |
| 283 | filePath, err := wavebase.ExpandHomeDir(identityFile) |
| 284 | if err != nil { |
| 285 | continue |
| 286 | } |
| 287 | privateKey, err := os.ReadFile(filePath) |
| 288 | if err != nil { |
| 289 | // skip this key and try with the next |
| 290 | continue |
| 291 | } |
| 292 | existingKeys[identityFile] = privateKey |
| 293 | identityFiles = append(identityFiles, identityFile) |
| 294 | } |
| 295 | // require pointer to modify list in closure |
| 296 | identityFilesPtr := &identityFiles |
| 297 | |
| 298 | var authSockSigners []ssh.Signer |
| 299 | authSockSigners = append(authSockSigners, authSockSignersExt...) |
| 300 | authSockSignersPtr := &authSockSigners |
| 301 | |
| 302 | return func() (outSigner []ssh.Signer, outErr error) { |
| 303 | defer func() { |
| 304 | panicErr := panichandler.PanicHandler("sshclient:publickey-callback", recover()) |
| 305 | if panicErr != nil { |
| 306 | outErr = panicErr |
| 307 | } |
| 308 | }() |
| 309 | // try auth sock |
| 310 | if len(*authSockSignersPtr) != 0 { |
| 311 | authSockSigner := (*authSockSignersPtr)[0] |
| 312 | *authSockSignersPtr = (*authSockSignersPtr)[1:] |
| 313 | return []ssh.Signer{authSockSigner}, nil |
| 314 | } |
| 315 | |
| 316 | if len(*identityFilesPtr) == 0 { |
| 317 | return nil, ConnectionError{ConnectionDebugInfo: debugInfo, Err: fmt.Errorf("no identity files remaining")} |
| 318 | } |
| 319 | identityFile := (*identityFilesPtr)[0] |
| 320 | blocklogger.Infof(connCtx, "[conndebug] trying keyfile %q...\n", identityFile) |
| 321 | *identityFilesPtr = (*identityFilesPtr)[1:] |
| 322 | privateKey, ok := existingKeys[identityFile] |
| 323 | if !ok { |
| 324 | log.Printf("error with existingKeys, this should never happen") |
| 325 | // skip this key and try with the next |
| 326 | return createDummySigner() |
| 327 | } |
| 328 | |
| 329 | unencryptedPrivateKey, err := ssh.ParseRawPrivateKey(privateKey) |
| 330 | if err == nil { |
| 331 | signer, err := ssh.NewSignerFromKey(unencryptedPrivateKey) |
| 332 | if err == nil { |
| 333 | if utilfn.SafeDeref(sshKeywords.SshAddKeysToAgent) && agentClient != nil { |
no test coverage detected