lib/api: Sanitize names used in certificates (fixes #7434) (#7435)

This commit is contained in:
Jakob Borg 2021-03-11 13:15:03 +01:00 committed by GitHub
parent cf838c71f7
commit df08984a58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 0 deletions

View File

@ -30,11 +30,15 @@ import (
"strconv"
"strings"
"time"
"unicode"
"github.com/julienschmidt/httprouter"
metrics "github.com/rcrowley/go-metrics"
"github.com/thejerf/suture/v4"
"github.com/vitrun/qart/qr"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
@ -153,6 +157,10 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
if err != nil {
name = s.tlsDefaultCommonName
}
name, err = sanitizedHostname(name)
if err != nil {
name = s.tlsDefaultCommonName
}
cert, err = tlsutil.NewCertificate(httpsCertFile, httpsKeyFile, name, httpsCertLifetimeDays)
}
@ -1895,6 +1903,38 @@ func errorString(err error) *string {
return nil
}
// sanitizedHostname returns the given name in a suitable form for use as
// the common name in a certificate, or an error.
func sanitizedHostname(name string) (string, error) {
// Remove diacritics and non-alphanumerics. This works by first
// transforming into normalization form D (things with diacriticals are
// split into the base character and the mark) and then removing
// undesired characters.
t := transform.Chain(
// Split runes with diacritics into base character and mark.
norm.NFD,
// Leave only [A-Za-z0-9-.].
runes.Remove(runes.Predicate(func(r rune) bool {
return r > unicode.MaxASCII ||
!unicode.IsLetter(r) && !unicode.IsNumber(r) &&
r != '.' && r != '-'
})))
name, _, err := transform.String(t, name)
if err != nil {
return "", err
}
// Name should not start or end with a dash or dot.
name = strings.Trim(name, "-.")
// Name should not be empty.
if name == "" {
return "", errors.New("no suitable name")
}
return strings.ToLower(name), nil
}
func isFolderNotFound(err error) bool {
for _, target := range []error{
model.ErrFolderMissing,

View File

@ -1370,6 +1370,27 @@ func TestConfigChanges(t *testing.T) {
}
}
func TestSanitizedHostname(t *testing.T) {
cases := []struct {
in, out string
}{
{"foo.BAR-baz", "foo.bar-baz"},
{"~.~-Min 1:a Räksmörgås-dator 😀😎 ~.~-", "min1araksmorgas-dator"},
{"Vicenç-PC", "vicenc-pc"},
{"~.~-~.~-", ""},
{"", ""},
}
for _, tc := range cases {
res, err := sanitizedHostname(tc.in)
if tc.out == "" && err == nil {
t.Errorf("%q should cause error", tc.in)
} else if res != tc.out {
t.Errorf("%q => %q, expected %q", tc.in, res, tc.out)
}
}
}
func equalStrings(a, b []string) bool {
if len(a) != len(b) {
return false