]> source.dussan.org Git - gitea.git/commitdiff
Implement #798 Flexible ssh-key input
authorPeter Smit <peter@smitmail.eu>
Fri, 2 Jan 2015 13:38:11 +0000 (15:38 +0200)
committerPeter Smit <peter@smitmail.eu>
Fri, 2 Jan 2015 13:38:11 +0000 (15:38 +0200)
It is now possible to input ssh keys in a number of formats: openssh, SSH2 or just the base64 encoded key.

models/publickey.go
routers/user/setting.go

index ba15ca455336586041647702eebf41e2be2bbd67..566814e841505055e702ac750ea37d9b974cba2f 100644 (file)
@@ -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")
index 419e84b395b806c366adb632db0afca9c528bcaa..953e61138fd5143feb521e38884724ee55a1fe4c 100644 (file)
@@ -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 {