You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ssh_key_fingerprint.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package asymkey
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/process"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/util"
  14. "golang.org/x/crypto/ssh"
  15. )
  16. // ___________.__ .__ __
  17. // \_ _____/|__| ____ ____ ________________________|__| _____/ |_
  18. // | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
  19. // | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
  20. // \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
  21. // \/ \//_____/ \/ |__| \/
  22. //
  23. // This file contains functions for fingerprinting SSH keys
  24. //
  25. // The database is used in checkKeyFingerprint however most of these functions probably belong in a module
  26. // checkKeyFingerprint only checks if key fingerprint has been used as public key,
  27. // it is OK to use same key as deploy key for multiple repositories/users.
  28. func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
  29. has, err := db.GetByBean(ctx, &PublicKey{
  30. Fingerprint: fingerprint,
  31. })
  32. if err != nil {
  33. return err
  34. } else if has {
  35. return ErrKeyAlreadyExist{0, fingerprint, ""}
  36. }
  37. return nil
  38. }
  39. func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
  40. // Calculate fingerprint.
  41. tmpPath, err := writeTmpKeyFile(publicKeyContent)
  42. if err != nil {
  43. return "", err
  44. }
  45. defer func() {
  46. if err := util.Remove(tmpPath); err != nil {
  47. log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
  48. }
  49. }()
  50. stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
  51. if err != nil {
  52. if strings.Contains(stderr, "is not a public key file") {
  53. return "", ErrKeyUnableVerify{stderr}
  54. }
  55. return "", fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
  56. } else if len(stdout) < 2 {
  57. return "", errors.New("not enough output for calculating fingerprint: " + stdout)
  58. }
  59. return strings.Split(stdout, " ")[1], nil
  60. }
  61. func calcFingerprintNative(publicKeyContent string) (string, error) {
  62. // Calculate fingerprint.
  63. pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
  64. if err != nil {
  65. return "", err
  66. }
  67. return ssh.FingerprintSHA256(pk), nil
  68. }
  69. // CalcFingerprint calculate public key's fingerprint
  70. func CalcFingerprint(publicKeyContent string) (string, error) {
  71. // Call the method based on configuration
  72. var (
  73. fnName, fp string
  74. err error
  75. )
  76. if setting.SSH.StartBuiltinServer {
  77. fnName = "calcFingerprintNative"
  78. fp, err = calcFingerprintNative(publicKeyContent)
  79. } else {
  80. fnName = "calcFingerprintSSHKeygen"
  81. fp, err = calcFingerprintSSHKeygen(publicKeyContent)
  82. }
  83. if err != nil {
  84. if IsErrKeyUnableVerify(err) {
  85. log.Info("%s", publicKeyContent)
  86. return "", err
  87. }
  88. return "", fmt.Errorf("%s: %w", fnName, err)
  89. }
  90. return fp, nil
  91. }