Cleanup previous PR

The overrides were not being passed to the askpass kitten.
And we dont need to support backward compatibility for secrets with no
backend, since this feature has never been released.
This commit is contained in:
Kovid Goyal
2025-09-30 20:38:44 +05:30
parent d02c63ac86
commit 460d10902f
5 changed files with 46 additions and 55 deletions

View File

@@ -137,6 +137,10 @@ Detailed list of changes
0.43.1 [future]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ssh kitten: Allow specifying a password and/or TOTP authentication secret to
automate interactive logins in scenarios where public key authentication is
not supported (:pull:`9020`)
- macOS: Fix a bug where the color of a transparent titlebar was off when
running in the release build versus the build from source. Also fix using a
transparent titlebar causing the background opacity to be doubled.

View File

@@ -73,7 +73,7 @@ func generateTOTP(secret string, digits, period int64, t time.Time) (string, err
off := sum[len(sum)-1] & 0x0f
code := (uint32(sum[off])&0x7f)<<24 | (uint32(sum[off+1])&0xff)<<16 | (uint32(sum[off+2])&0xff)<<8 | (uint32(sum[off+3]) & 0xff)
mod := uint32(1)
for i := int64(0); i < digits; i++ {
for range digits {
mod *= 10
}
val := code % mod
@@ -96,15 +96,11 @@ func RunSSHAskpass() int {
host := os.Getenv("KITTY_SSH_ASKPASS_HOST")
user := os.Getenv("KITTY_SSH_ASKPASS_USER")
if host != "" {
if cfg, bad_lines, err := load_config(host, user, nil); err == nil && cfg != nil {
for _, bl := range bad_lines {
if bl.Err != nil {
// Only fail for our secret backend errors to avoid
// unrelated ssh.conf issues breaking askpass.
if strings.Contains(bl.Err.Error(), "Unsupported secret backend") {
fatal(bl.Err)
}
}
var overrides []string
_ = json.Unmarshal([]byte(os.Getenv("KITTY_SSH_ASKPASS_OVERRIDES")), &overrides)
if cfg, _, err := load_config(host, user, overrides); err == nil && cfg != nil {
if err = resolve_secrets(cfg, false); err != nil {
return fatal(err)
}
// Password autofill
if isPasswordPrompt(msg) && cfg.Password != "" {

View File

@@ -24,10 +24,7 @@ import (
var _ = fmt.Print
// parseSecretSetting parses values of the form "backend:secret" for settings where
// only the "text" backend is currently supported. If no backend is specified,
// the value is treated as a plain text secret for backward compatibility.
func parseSecretSetting(settingKey, val string) (string, error) {
func resolve_secret(key, val string) (string, error) {
v := strings.TrimSpace(val)
if v == "" {
return "", nil
@@ -39,11 +36,25 @@ func parseSecretSetting(settingKey, val string) (string, error) {
case "text":
return s, nil
default:
return "", fmt.Errorf("Unsupported secret backend %q for %s. Supported backends: text", b, settingKey)
return "", fmt.Errorf("Unsupported secret backend %s for %s. Supported backends: text", b, key)
}
}
// No backend specified; treat as text
return v, nil
return "", fmt.Errorf("No secret backend specified for: %s", key)
}
func resolve_secrets(c *Config, only_syntax bool) error {
_ = only_syntax // this will be useful when using backends that require user interaction
if r, err := resolve_secret("password", c.Password); err != nil {
return err
} else {
c.Password = r
}
if r, err := resolve_secret("totp_secret", c.Totp_secret); err != nil {
return err
} else {
c.Password = r
}
return nil
}
type EnvInstruction struct {
@@ -369,7 +380,7 @@ type ConfigSet struct {
func config_for_hostname(hostname_to_match, username_to_match string, cs *ConfigSet) *Config {
matcher := func(q *Config) bool {
for _, pat := range strings.Split(q.Hostname, " ") {
for pat := range strings.SplitSeq(q.Hostname, " ") {
upat := "*"
if strings.Contains(pat, "@") {
upat, pat, _ = strings.Cut(pat, "@")
@@ -401,22 +412,6 @@ func (self *ConfigSet) line_handler(key, val string) error {
c = NewConfig()
self.all_configs = append(self.all_configs, c)
}
switch key {
case "password":
secret, err := parseSecretSetting("password", val)
if err != nil {
return err
}
c.Password = secret
return nil
case "totp_secret":
secret, err := parseSecretSetting("totp_secret", val)
if err != nil {
return err
}
c.Totp_secret = secret
return nil
}
return c.Parse(key, val)
}
@@ -438,16 +433,5 @@ func load_config(hostname_to_match string, username_to_match string, overrides [
bad_lines = append(bad_lines, override_parser.BadLines()...)
final_conf.Hostname = h
}
// Normalize and validate secrets post-overrides as overrides bypass line_handler
if s, err := parseSecretSetting("password", final_conf.Password); err != nil {
bad_lines = append(bad_lines, config.ConfigLine{Src_file: "<overrides>", Line: "password " + final_conf.Password, Line_number: 0, Err: err})
} else {
final_conf.Password = s
}
if s, err := parseSecretSetting("totp_secret", final_conf.Totp_secret); err != nil {
bad_lines = append(bad_lines, config.ConfigLine{Src_file: "<overrides>", Line: "totp_secret " + final_conf.Totp_secret, Line_number: 0, Err: err})
} else {
final_conf.Totp_secret = s
}
return final_conf, bad_lines, nil
}

View File

@@ -145,7 +145,7 @@ func connection_sharing_args(kitty_pid int) ([]string, error) {
}, nil
}
func set_askpass() (need_to_request_data bool) {
func set_askpass(hostname_for_match, uname string, overrides []string) (need_to_request_data bool) {
need_to_request_data = true
sentinel := filepath.Join(utils.CacheDir(), "openssh-is-new-enough-for-askpass")
_, err := os.Stat(sentinel)
@@ -160,6 +160,11 @@ func set_askpass() (need_to_request_data bool) {
if err == nil {
os.Setenv("SSH_ASKPASS", exe)
os.Setenv("KITTY_KITTEN_RUN_MODULE", "ssh_askpass")
// Provide data to askpass so it can lookup auth settings in ssh.conf
os.Setenv("KITTY_SSH_ASKPASS_HOST", hostname_for_match)
os.Setenv("KITTY_SSH_ASKPASS_USER", uname)
ov, _ := json.Marshal(overrides)
os.Setenv("KITTY_SSH_ASKPASS_OVERRIDES", string(ov))
if !need_to_request_data {
os.Setenv("SSH_ASKPASS_REQUIRE", "force")
}
@@ -621,6 +626,11 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
if err != nil {
return 1, err
}
// check the secrets syntax here as askpass has no good way to report errors to
// the user
if err = resolve_secrets(host_opts, true); err != nil {
return 1, err
}
if len(bad_lines) > 0 {
for _, x := range bad_lines {
fmt.Fprintf(os.Stderr, "Ignoring bad config line: %s:%d with error: %s", filepath.Base(x.Src_file), x.Line_number, x.Err)
@@ -646,14 +656,11 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
}
cmd = slices.Insert(cmd, insertion_point, control_master_args...)
}
use_kitty_askpass := host_opts.Askpass == Askpass_native || (host_opts.Askpass == Askpass_unless_set && os.Getenv("SSH_ASKPASS") == "")
need_to_request_data := true
if use_kitty_askpass {
need_to_request_data = set_askpass()
// Provide host/user to askpass so it can lookup auth settings in ssh.conf
os.Setenv("KITTY_SSH_ASKPASS_HOST", hostname_for_match)
os.Setenv("KITTY_SSH_ASKPASS_USER", uname)
}
use_kitty_askpass := host_opts.Askpass == Askpass_native || (host_opts.Askpass == Askpass_unless_set && os.Getenv("SSH_ASKPASS") == "")
need_to_request_data := true
if use_kitty_askpass {
need_to_request_data = set_askpass(hostname_for_match, uname, overrides)
}
master_is_functional := func() bool {
if master_checked {
return master_is_alive

View File

@@ -223,7 +223,7 @@ egr() # }}}
agr('askpass', 'Askpass automation') # {{{
opt('password', '', long_text='''
Specify a secret to use when SSH prompts for a password. The value format is
Specify a password to use when SSH prompts for a password. The value format is
"backend:secret". Currently, only the "text" backend is supported, which stores
the secret in plain text in the config file. For example: