Commit ff4c10a1 authored by Kevin Lyda's avatar Kevin Lyda
Browse files

Clean up deprecated key functions

Remove the file-only routines.
parent 6ffc448b
Loading
Loading
Loading
Loading
+23 −111
Original line number Diff line number Diff line
@@ -22,8 +22,11 @@ import (

var keytemplate = `command="%s --user %s",restrict,pty %s`

// Add adds an ssh key to the `authorized_keys` file.
func Add(login, public string) error {
// ErrDuplicateKey error when an existing key is added to the db.
var ErrDuplicateKey = errors.New("key already exists")

// addToFile adds an ssh key to the `authorized_keys` file.
func addToFile(login, public string) error {
	// Parse and verify the key.
	public = strings.TrimSpace(public)
	theKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(public))
@@ -63,7 +66,7 @@ func Add(login, public string) error {
		return err
	}
	if bytes.Contains(keycontent, ssh.MarshalAuthorizedKey(theKey)) {
		return errors.New("key already exists")
		return ErrDuplicateKey
	}

	// Generate and write the key.
@@ -80,69 +83,8 @@ func Add(login, public string) error {
	return nil
}

// List returns a list of ssh keys for this user.
func List(login string) ([]string, error) {
	keys := []string{}

	// Find the bulletin binary.
	bulletin, err := os.Executable()
	if err != nil {
		return keys, err
	}

	// File system management.
	sshdir := path.Join(xdg.Home, ".ssh")
	err = os.MkdirAll(sshdir, 0700)
	if err != nil {
		return keys, err
	}
	keyfile := path.Join(sshdir, "authorized_keys")

	// Open the authorized_keys file.
	f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home
	if err != nil {
		return keys, err
	}
	defer f.Close()

	// look for lines.
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		keyline := bytes.TrimSpace(scanner.Bytes())
		if len(keyline) == 0 {
			continue
		}
		public, _, options, _, err := ssh.ParseAuthorizedKey([]byte(keyline))
		if err != nil {
			return keys, err
		}
		for i := range options {
			opts := strings.SplitN(options[i], "=", 2)
			if len(opts) != 2 || opts[0] != "command" {
				continue
			}
			cmd := strings.Split(strings.Trim(opts[1], "\" "), " ")
			if len(cmd) != 3 {
				return keys, fmt.Errorf("unexpected command in authorized keys file (%s)", opts[1])
			}
			if cmd[0] != bulletin {
				return keys, fmt.Errorf("unexpected bulletin in authorized keys file (%s)", opts[1])
			}
			if cmd[1] != "-u" {
				return keys, fmt.Errorf("unexpected flag in authorized keys file (%s)", opts[1])
			}
			if cmd[2] == login {
				keys = append(keys, strings.Trim(string(ssh.MarshalAuthorizedKey(public)), "\n"))
			}
			break
		}
	}

	return keys, nil
}

// Delete removes the key.
func Delete(public string) error {
// deleteFromFile removes a key from the `authorized_keys` file.
func deleteFromFile(public string) error {
	keys := []string{}

	// Parse and verify the key.
@@ -201,46 +143,9 @@ func Delete(public string) error {
	return nil
}

// Fetch fetches keys and adds them.
func Fetch(login, nickname, username string) string {
	sites := map[string]string{
		"codeberg": "https://codeberg.org/%s.keys",
		"gitlab":   "https://gitlab.com/%s.keys",
		"github":   "https://github.com/%s.keys",
	}
	site := sites[strings.ToLower(nickname)]
	if site == "" {
		return fmt.Sprintln("ERROR: site nickname unknown.")
	}
	url := fmt.Sprintf(site, username)
	resp, err := http.Get(url) // #nosec G107 -- URL is constructed from a hardcoded allowlist of sites
	if err != nil {
		return fmt.Sprintf("ERROR: Failed to fetch ssh keys (%s).\n", err)
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	keys := 0
	for scanner.Scan() {
		keyline := string(bytes.TrimSpace(scanner.Bytes()))
		if err := Add(strings.ToUpper(login), keyline); err != nil {
			return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err)
		}
		keys++
	}
	switch keys {
	case 0:
		return fmt.Sprintln("No keys added.")
	case 1:
		return fmt.Sprintln("Key is added.")
	default:
		return fmt.Sprintf("%d keys added.\n", keys)
	}
}

// AddDB adds an SSH key to both the authorized_keys file and the database.
func AddDB(q *storage.Queries, login, public string) error {
	// First add to authorized_keys file (existing behavior).
	if err := Add(login, public); err != nil {
	if err := addToFile(login, public); err != nil {
		return err
	}

@@ -284,12 +189,10 @@ func ListDB(q *storage.Queries, login string) ([]storage.SshKey, error) {

// DeleteDB removes an SSH key from both the authorized_keys file and the database.
func DeleteDB(q *storage.Queries, fingerprint, public string) error {
	// Delete from authorized_keys file.
	if err := Delete(public); err != nil {
	if err := deleteFromFile(public); err != nil {
		return err
	}

	// Delete from database.
	ctx := storage.Context()
	return q.DeleteSSHKey(ctx, fingerprint)
}
@@ -317,7 +220,7 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string {
	for scanner.Scan() {
		keyline := string(bytes.TrimSpace(scanner.Bytes()))
		if err := AddDB(q, strings.ToUpper(login), keyline); err != nil {
			if err.Error() == "key already exists" {
			if errors.Is(err, ErrDuplicateKey) {
				dups++
			} else {
				return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err)
@@ -326,12 +229,21 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string {
			keys++
		}
	}
	var dupsStr string
	switch dups {
	case 0:
		dupsStr = ".\n"
	case 1:
		dupsStr = " (1 duplicate skipped).\n"
	default:
		dupsStr = fmt.Sprintf(" (%d duplicates skipped).\n", dups)
	}
	switch keys {
	case 0:
		return fmt.Sprintln("No keys added.")
		return fmt.Sprintf("No keys added%s", dupsStr)
	case 1:
		return fmt.Sprintln("Key is added.")
		return fmt.Sprintf("Key is added%s", dupsStr)
	default:
		return fmt.Sprintf("%d new keys added (%d duplicates skipped).\n", keys, dups)
		return fmt.Sprintf("%d new keys added%s", keys, dupsStr)
	}
}