From a50a0bc5caa00cb344631621d7cecdab940886da Mon Sep 17 00:00:00 2001 From: Thomas Miceli Date: Mon, 27 May 2024 23:40:17 +0200 Subject: [PATCH] Fix permissions for SSH --- internal/actions/actions.go | 2 +- internal/db/sshkey.go | 13 ++++++------- internal/db/user.go | 9 +++++++++ internal/i18n/locales/en-US.yml | 1 + internal/ssh/git_ssh.go | 11 +++++++++-- internal/ssh/run.go | 4 ++-- internal/web/git_http.go | 11 +++++++++-- internal/web/server.go | 2 +- internal/web/settings.go | 8 ++++++++ internal/web/test/auth_test.go | 6 +----- 10 files changed, 47 insertions(+), 20 deletions(-) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 404ae66f..5c8b460d 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -74,7 +74,7 @@ func Run(actionType int) { case IndexGists: functionToRun = indexGists default: - panic("unhandled default case") + log.Error().Msg("Unknown action type") } functionToRun() diff --git a/internal/db/sshkey.go b/internal/db/sshkey.go index e79d9739..0ff1a2ae 100644 --- a/internal/db/sshkey.go +++ b/internal/db/sshkey.go @@ -48,13 +48,12 @@ func GetSSHKeyByID(sshKeyId uint) (*SSHKey, error) { return sshKey, err } -func SSHKeyDoesExists(sshKeyContent string) (*SSHKey, error) { - sshKey := new(SSHKey) - err := db. - Where("content like ?", sshKeyContent+"%"). - First(&sshKey).Error - - return sshKey, err +func SSHKeyDoesExists(sshKeyContent string) (bool, error) { + var count int64 + err := db.Model(&SSHKey{}). + Where("content = ?", sshKeyContent). + Count(&count).Error + return count > 0, err } func (sshKey *SSHKey) Create() error { diff --git a/internal/db/user.go b/internal/db/user.go index 88208870..37a01912 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -118,6 +118,15 @@ func GetUsersFromEmails(emailsSet map[string]struct{}) (map[string]*User, error) return userMap, nil } +func GetUserFromSSHKey(sshKey string) (*User, error) { + user := new(User) + err := db. + Joins("JOIN ssh_keys ON users.id = ssh_keys.user_id"). + Where("ssh_keys.content = ?", sshKey). + First(&user).Error + return user, err +} + func SSHKeyExistsForUser(sshKey string, userId uint) (*SSHKey, error) { key := new(SSHKey) err := db. diff --git a/internal/i18n/locales/en-US.yml b/internal/i18n/locales/en-US.yml index 9a34da6f..55d7cf06 100644 --- a/internal/i18n/locales/en-US.yml +++ b/internal/i18n/locales/en-US.yml @@ -126,6 +126,7 @@ settings.delete-ssh-key-confirm: Confirm deletion of SSH key settings.ssh-key-added-at: Added settings.ssh-key-never-used: Never used settings.ssh-key-last-used: Last used +settings.ssh-key-exists: SSH key already exists settings.change-username: Change username settings.create-password: Create password settings.create-password-help: Create your password to login to Opengist via HTTP diff --git a/internal/ssh/git_ssh.go b/internal/ssh/git_ssh.go index caf9c9f6..f90a50bd 100644 --- a/internal/ssh/git_ssh.go +++ b/internal/ssh/git_ssh.go @@ -50,11 +50,18 @@ func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error { // - gist is not found (obfuscation) // - admin setting to require login is set to true if verb == "receive-pack" || - gist.Private == 2 || + gist.Private == db.PrivateVisibility || gist.ID == 0 || !allowUnauthenticated { - pubKey, err := db.SSHKeyExistsForUser(key, gist.UserID) + var userToCheckPermissions *db.User + if gist.Private != db.PrivateVisibility && verb == "upload-pack" { + userToCheckPermissions, err = db.GetUserFromSSHKey(key) + } else { + userToCheckPermissions = &gist.User + } + + pubKey, err := db.SSHKeyExistsForUser(key, userToCheckPermissions.ID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { log.Warn().Msg("Invalid SSH authentication attempt from " + ip) diff --git a/internal/ssh/run.go b/internal/ssh/run.go index e4eaccb0..6d624384 100644 --- a/internal/ssh/run.go +++ b/internal/ssh/run.go @@ -24,8 +24,8 @@ func Start() { sshConfig := &ssh.ServerConfig{ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { strKey := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))) - _, err := db.SSHKeyDoesExists(strKey) - if err != nil { + exists, err := db.SSHKeyDoesExists(strKey) + if !exists { if !errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } diff --git a/internal/web/git_http.go b/internal/web/git_http.go index bf8bbf88..236ba4fb 100644 --- a/internal/web/git_http.go +++ b/internal/web/git_http.go @@ -73,7 +73,7 @@ func gitHttp(ctx echo.Context) error { allow, err := auth.ShouldAllowUnauthenticatedGistAccess(ContextAuthInfo{ctx}, true) if err != nil { - panic("impossible") + log.Fatal().Err(err).Msg("Cannot check if unauthenticated access is allowed") } // Shows basic auth if : @@ -105,7 +105,14 @@ func gitHttp(ctx echo.Context) error { return plainText(ctx, 404, "Check your credentials or make sure you have access to the Gist") } - if ok, err := utils.Argon2id.Verify(authPassword, gist.User.Password); !ok || gist.User.Username != authUsername { + var userToCheckPermissions *db.User + if gist.Private != db.PrivateVisibility && isPull { + userToCheckPermissions, err = db.GetUserByUsername(authUsername) + } else { + userToCheckPermissions = &gist.User + } + + if ok, err := utils.Argon2id.Verify(authPassword, userToCheckPermissions.Password); !ok { if err != nil { return errorRes(500, "Cannot verify password", err) } diff --git a/internal/web/server.go b/internal/web/server.go index 9da58485..166334a3 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -526,7 +526,7 @@ func makeCheckRequireLogin(isSingleGistAccess bool) echo.MiddlewareFunc { allow, err := auth.ShouldAllowUnauthenticatedGistAccess(ContextAuthInfo{ctx}, isSingleGistAccess) if err != nil { - panic("impossible") + log.Fatal().Err(err).Msg("Failed to check if unauthenticated access is allowed") } if !allow { diff --git a/internal/web/settings.go b/internal/web/settings.go index 14d64802..d3d814bb 100644 --- a/internal/web/settings.go +++ b/internal/web/settings.go @@ -89,6 +89,14 @@ func sshKeysProcess(ctx echo.Context) error { } key.Content = strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey))) + if exists, err := db.SSHKeyDoesExists(key.Content); exists { + if err != nil { + return errorRes(500, "Cannot check if SSH key exists", err) + } + addFlash(ctx, tr(ctx, "settings.ssh-key-exists"), "error") + return redirect(ctx, "/settings") + } + if err := key.Create(); err != nil { return errorRes(500, "Cannot add SSH key", err) } diff --git a/internal/web/test/auth_test.go b/internal/web/test/auth_test.go index 655a36d9..64d159a5 100644 --- a/internal/web/test/auth_test.go +++ b/internal/web/test/auth_test.go @@ -153,7 +153,7 @@ func TestAnonymous(t *testing.T) { } -func TestGitClonePull(t *testing.T) { +func TestGitOperations(t *testing.T) { setup(t) s, err := newTestServer() require.NoError(t, err, "Failed to create test server") @@ -161,11 +161,7 @@ func TestGitClonePull(t *testing.T) { admin := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, admin) - - // err = s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200) - // require.NoError(t, err) s.sessionCookie = "" - register(t, s, db.UserDTO{Username: "fujiwara", Password: "fujiwara"}) s.sessionCookie = "" register(t, s, db.UserDTO{Username: "kaguya", Password: "kaguya"})