aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author无闻 <u@gogs.io>2015-01-22 19:39:35 +0800
committer无闻 <u@gogs.io>2015-01-22 19:39:35 +0800
commit161774d4fb61d1c2b28a90812b15d077312083f3 (patch)
tree993e163f35713461e8b80a8918ff70c197c78f2c
parentedbfbfa66e451aa5fd899c94ff21ac468f6945bb (diff)
parent6251626de45a2310b3532176c0fc12c52dbd6e32 (diff)
downloadgitea-161774d4fb61d1c2b28a90812b15d077312083f3.tar.gz
gitea-161774d4fb61d1c2b28a90812b15d077312083f3.zip
Merge pull request #825 from phsmit/ssh2_keys
Implement #798 Flexible ssh-key input
-rw-r--r--models/publickey.go81
-rw-r--r--routers/user/setting.go13
2 files changed, 90 insertions, 4 deletions
diff --git a/models/publickey.go b/models/publickey.go
index ba15ca4553..566814e841 100644
--- a/models/publickey.go
+++ b/models/publickey.go
@@ -6,6 +6,8 @@ package models
import (
"bufio"
+ "encoding/base64"
+ "encoding/binary"
"errors"
"fmt"
"io"
@@ -111,6 +113,85 @@ var (
}
)
+func extractTypeFromBase64Key(key string) (string, error) {
+ b, err := base64.StdEncoding.DecodeString(key)
+ if err != nil || len(b) < 4 {
+ return "", errors.New("Invalid key format")
+ }
+
+ keyLength := int(binary.BigEndian.Uint32(b))
+
+ if len(b) < 4+keyLength {
+ return "", errors.New("Invalid key format")
+ }
+
+ return string(b[4 : 4+keyLength]), nil
+}
+
+// Parse any key string in openssh or ssh2 format to clean openssh string (rfc4253)
+func ParseKeyString(content string) (string, error) {
+
+ // Transform all legal line endings to a single "\n"
+ s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1)
+
+ lines := strings.Split(s, "\n")
+
+ var keyType, keyContent, keyComment string
+
+ if len(lines) == 1 {
+ // Parse openssh format
+ parts := strings.Fields(lines[0])
+ switch len(parts) {
+ case 0:
+ return "", errors.New("Empty key")
+ case 1:
+ keyContent = parts[0]
+ case 2:
+ keyType = parts[0]
+ keyContent = parts[1]
+ default:
+ keyType = parts[0]
+ keyContent = parts[1]
+ keyComment = parts[2]
+ }
+
+ // If keyType is not given, extract it from content. If given, validate it
+ if len(keyType) == 0 {
+ if t, err := extractTypeFromBase64Key(keyContent); err == nil {
+ keyType = t
+ } else {
+ return "", err
+ }
+ } else {
+ if t, err := extractTypeFromBase64Key(keyContent); err != nil || keyType != t {
+ return "", err
+ }
+ }
+ } else {
+ // Parse SSH2 file format.
+ continuationLine := false
+
+ for _, line := range lines {
+ // Skip lines that:
+ // 1) are a continuation of the previous line,
+ // 2) contain ":" as that are comment lines
+ // 3) contain "-" as that are begin and end tags
+ if continuationLine || strings.ContainsAny(line, ":-") {
+ continuationLine = strings.HasSuffix(line, "\\")
+ } else {
+ keyContent = keyContent + line
+ }
+ }
+
+ if t, err := extractTypeFromBase64Key(keyContent); err == nil {
+ keyType = t
+ } else {
+ return "", err
+ }
+ }
+ return keyType + " " + keyContent + " " + keyComment, nil
+}
+
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
func CheckPublicKeyString(content string) (bool, error) {
content = strings.TrimRight(content, "\n\r")
diff --git a/routers/user/setting.go b/routers/user/setting.go
index 419e84b395..953e61138f 100644
--- a/routers/user/setting.go
+++ b/routers/user/setting.go
@@ -325,10 +325,15 @@ func SettingsSSHKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
return
}
- // Remove newline characters from form.KeyContent
- cleanContent := strings.Replace(form.Content, "\n", "", -1)
+ // Parse openssh style string from form content
+ content, err := models.ParseKeyString(form.Content)
+ if err != nil {
+ ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
+ ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh")
+ return
+ }
- if ok, err := models.CheckPublicKeyString(cleanContent); !ok {
+ if ok, err := models.CheckPublicKeyString(content); !ok {
if err == models.ErrKeyUnableVerify {
ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
} else {
@@ -341,7 +346,7 @@ func SettingsSSHKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
k := &models.PublicKey{
OwnerId: ctx.User.Id,
Name: form.SSHTitle,
- Content: cleanContent,
+ Content: content,
}
if err := models.AddPublicKey(k); err != nil {
if err == models.ErrKeyAlreadyExist {