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

Fix key migration

parent abccdc93
Loading
Loading
Loading
Loading
+53 −6
Original line number Diff line number Diff line
@@ -217,6 +217,7 @@ Match User %s
    X11Forwarding no
    AllowTcpForwarding no
    PermitTunnel no
    AuthorizedKeysFile none
    ForceCommand /usr/sbin/nologin
    AuthorizedKeysCommand %s authorized-keys %%u %%t %%k
    AuthorizedKeysCommandUser %s
@@ -286,7 +287,9 @@ func MigrateKeys() int {
}

// MigrateKeysWithQueries migrates authorized_keys entries into the
// ssh_keys database table using the provided query handle.
// ssh_keys database table using the provided query handle.  It also
// rewrites the authorized_keys file so that all bulletin entries use
// the current secure format (--user and restrict).
func MigrateKeysWithQueries(q *storage.Queries) error {
	ctx := storage.Context()

@@ -304,6 +307,8 @@ func MigrateKeysWithQueries(q *storage.Queries) error {

	migrated := 0
	skipped := 0
	var nonBulletinLines []string
	var rewriteLines []string
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := bytes.TrimSpace(scanner.Bytes())
@@ -313,29 +318,57 @@ func MigrateKeysWithQueries(q *storage.Queries) error {

		pubKey, _, options, _, err := ssh.ParseAuthorizedKey(line)
		if err != nil {
			fmt.Printf("WARNING: skipping unparseable line: %s\n", err)
			skipped++
			fmt.Printf("WARNING: keeping unparseable line as-is: %s\n", err)
			nonBulletinLines = append(nonBulletinLines, string(line))
			continue
		}

		// Extract login from the command= option.
		// Extract login from the command= option.  Accept both the
		// old format (-u LOGIN) and the new format (--user LOGIN).
		login := ""
		isBulletin := false
		for _, opt := range options {
			parts := strings.SplitN(opt, "=", 2)
			if len(parts) != 2 || parts[0] != "command" {
				continue
			}
			cmd := strings.Split(strings.Trim(parts[1], "\" "), " ")
			if len(cmd) >= 3 && cmd[0] == bulletin && cmd[1] == "-u" {
			if len(cmd) >= 3 && cmd[0] == bulletin {
				if cmd[1] == "-u" || cmd[1] == "--user" {
					login = cmd[2]
					isBulletin = true
				}
			}
			break
		}
		if !isBulletin {
			nonBulletinLines = append(nonBulletinLines, string(line))
			continue
		}
		if login == "" {
			skipped++
			continue
		}

		// Ensure the user exists in the DB.
		u, err := q.GetUser(ctx, login)
		if err != nil {
			fmt.Printf("WARNING: skipping key for %s: %s\n", login, err)
			skipped++
			continue
		}
		if u.Login == "" {
			_, err = q.AddUser(ctx, storage.AddUserParams{
				Login: login,
			})
			if err != nil {
				fmt.Printf("WARNING: could not create user %s: %s\n", login, err)
				skipped++
				continue
			}
			fmt.Printf("Created user %s.\n", login)
		}

		// Compute fingerprint and key details.
		h := sha256.Sum256(pubKey.Marshal())
		fp := "SHA256:" + base64.RawStdEncoding.EncodeToString(h[:])
@@ -364,12 +397,26 @@ func MigrateKeysWithQueries(q *storage.Queries) error {
			skipped++
			continue
		}

		// Write this entry back in the new secure format.
		newLine := fmt.Sprintf("command=\"%s --user %s\",restrict %s",
			bulletin, login, keyLine)
		rewriteLines = append(rewriteLines, newLine)
		migrated++
	}
	if err := scanner.Err(); err != nil {
		return fmt.Errorf("error reading authorized_keys: %w", err)
	}

	// Rewrite the authorized_keys file with upgraded entries.
	allLines := append(nonBulletinLines, rewriteLines...)
	newContent := strings.Join(allLines, "\n") + "\n"
	err = os.WriteFile(keyfile, []byte(newContent), 0600) // #nosec G703 -- path is constructed from xdg.Home
	if err != nil {
		return fmt.Errorf("failed to rewrite %s: %w", keyfile, err)
	}

	fmt.Printf("Migrated %d keys, skipped %d.\n", migrated, skipped)
	fmt.Println("The authorized_keys file has been rewritten with secure entries.")
	return nil
}