2021-07-24 13:16:34 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2021-07-24 13:16:34 +03:00
2021-12-10 11:14:24 +03:00
package asymkey
2021-07-24 13:16:34 +03:00
import (
2022-05-20 17:08:52 +03:00
"context"
2021-07-24 13:16:34 +03:00
"errors"
"fmt"
"strings"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-07-24 13:16:34 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
2021-11-17 15:34:35 +03:00
2021-07-24 13:16:34 +03:00
"golang.org/x/crypto/ssh"
)
// ___________.__ .__ __
// \_ _____/|__| ____ ____ ________________________|__| _____/ |_
// | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
// | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
// \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
// \/ \//_____/ \/ |__| \/
//
// This file contains functions for fingerprinting SSH keys
//
// The database is used in checkKeyFingerprint however most of these functions probably belong in a module
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users.
2022-05-20 17:08:52 +03:00
func checkKeyFingerprint ( ctx context . Context , fingerprint string ) error {
has , err := db . GetByBean ( ctx , & PublicKey {
2021-07-24 13:16:34 +03:00
Fingerprint : fingerprint ,
} )
if err != nil {
return err
} else if has {
return ErrKeyAlreadyExist { 0 , fingerprint , "" }
}
return nil
}
func calcFingerprintSSHKeygen ( publicKeyContent string ) ( string , error ) {
// Calculate fingerprint.
tmpPath , err := writeTmpKeyFile ( publicKeyContent )
if err != nil {
return "" , err
}
defer func ( ) {
if err := util . Remove ( tmpPath ) ; err != nil {
log . Warn ( "Unable to remove temporary key file: %s: Error: %v" , tmpPath , err )
}
} ( )
stdout , stderr , err := process . GetManager ( ) . Exec ( "AddPublicKey" , "ssh-keygen" , "-lf" , tmpPath )
if err != nil {
if strings . Contains ( stderr , "is not a public key file" ) {
return "" , ErrKeyUnableVerify { stderr }
}
return "" , fmt . Errorf ( "'ssh-keygen -lf %s' failed with error '%s': %s" , tmpPath , err , stderr )
} else if len ( stdout ) < 2 {
return "" , errors . New ( "not enough output for calculating fingerprint: " + stdout )
}
return strings . Split ( stdout , " " ) [ 1 ] , nil
}
func calcFingerprintNative ( publicKeyContent string ) ( string , error ) {
// Calculate fingerprint.
pk , _ , _ , _ , err := ssh . ParseAuthorizedKey ( [ ] byte ( publicKeyContent ) )
if err != nil {
return "" , err
}
return ssh . FingerprintSHA256 ( pk ) , nil
}
2022-06-04 22:18:50 +03:00
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint ( publicKeyContent string ) ( string , error ) {
2021-07-24 13:16:34 +03:00
// Call the method based on configuration
var (
fnName , fp string
err error
)
if setting . SSH . StartBuiltinServer {
fnName = "calcFingerprintNative"
fp , err = calcFingerprintNative ( publicKeyContent )
} else {
fnName = "calcFingerprintSSHKeygen"
fp , err = calcFingerprintSSHKeygen ( publicKeyContent )
}
if err != nil {
if IsErrKeyUnableVerify ( err ) {
log . Info ( "%s" , publicKeyContent )
return "" , err
}
2022-10-24 22:29:17 +03:00
return "" , fmt . Errorf ( "%s: %w" , fnName , err )
2021-07-24 13:16:34 +03:00
}
return fp , nil
}