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. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package asymkey
  5. import (
  6. "context"
  7. "errors"
  8. "fmt"
  9. "strings"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/process"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/util"
  15. "golang.org/x/crypto/ssh"
  16. )
  17. // ___________.__ .__ __
  18. // \_ _____/|__| ____ ____ ________________________|__| _____/ |_
  19. // | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
  20. // | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
  21. // \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
  22. // \/ \//_____/ \/ |__| \/
  23. //
  24. // This file contains functions for fingerprinting SSH keys
  25. //
  26. // The database is used in checkKeyFingerprint however most of these functions probably belong in a module
  27. // checkKeyFingerprint only checks if key fingerprint has been used as public key,
  28. // it is OK to use same key as deploy key for multiple repositories/users.
  29. func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
  30. has, err := db.GetByBean(ctx, &PublicKey{
  31. Fingerprint: fingerprint,
  32. })
  33. if err != nil {
  34. return err
  35. } else if has {
  36. return ErrKeyAlreadyExist{0, fingerprint, ""}
  37. }
  38. return nil
  39. }
  40. func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
  41. // Calculate fingerprint.
  42. tmpPath, err := writeTmpKeyFile(publicKeyContent)
  43. if err != nil {
  44. return "", err
  45. }
  46. defer func() {
  47. if err := util.Remove(tmpPath); err != nil {
  48. log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
  49. }
  50. }()
  51. stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
  52. if err != nil {
  53. if strings.Contains(stderr, "is not a public key file") {
  54. return "", ErrKeyUnableVerify{stderr}
  55. }
  56. return "", fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
  57. } else if len(stdout) < 2 {
  58. return "", errors.New("not enough output for calculating fingerprint: " + stdout)
  59. }
  60. return strings.Split(stdout, " ")[1], nil
  61. }
  62. func calcFingerprintNative(publicKeyContent string) (string, error) {
  63. // Calculate fingerprint.
  64. pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
  65. if err != nil {
  66. return "", err
  67. }
  68. return ssh.FingerprintSHA256(pk), nil
  69. }
  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: %v", fnName, err)
  89. }
  90. return fp, nil
  91. }