Browse Source

add unixcrypt build tag

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Nicola Murino 2 years ago
parent
commit
ee90bfb506

+ 1 - 1
.github/workflows/development.yml

@@ -236,7 +236,7 @@ jobs:
 
       - name: Build
         run: |
-          go build -trimpath -tags nopgxregisterdefaulttypes,nogcs,nos3,noportable,nobolt,nomysql,nopgsql,nosqlite,nometrics,noazblob -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
+          go build -trimpath -tags nopgxregisterdefaulttypes,nogcs,nos3,noportable,nobolt,nomysql,nopgsql,nosqlite,nometrics,noazblob,unixcrypt -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
           ./sftpgo -v
           cp -r openapi static templates internal/bundle/
           go build -trimpath -tags nopgxregisterdefaulttypes,bundle -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo

+ 3 - 0
.github/workflows/docker.yml

@@ -72,6 +72,9 @@ jobs:
           elif [[ $DOCKER_PKG == debian-plugins ]]; then
             VERSION="${VERSION}-plugins"
             VERSION_SLIM="${VERSION}-slim"
+            FEATURES="${FEATURES},unixcrypt"
+          elif [[ $DOCKER_PKG == debian ]]; then
+            FEATURES="${FEATURES},unixcrypt"
           fi
           DOCKER_IMAGES=("drakkan/sftpgo" "ghcr.io/drakkan/sftpgo")
           TAGS="${DOCKER_IMAGES[0]}:${VERSION}"

+ 1 - 0
docs/build-from-source.md

@@ -14,6 +14,7 @@ The following build tags are available:
 - `noportable`, disable portable mode, default enabled
 - `nometrics`, disable Prometheus metrics, default enabled
 - `bundle`, embed static files and templates. Before building with this tag enabled you have to copy `openapi`, `static` and `templates` dirs to `internal/bundle` directory. Default disabled
+- `unixcrypt`, enable linking to `libcrypt`, default disabled, requires `CGO`
 
 If no build tag is specified the build will include the default features.
 

+ 2 - 1
go.mod

@@ -8,6 +8,7 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
 	github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
 	github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
+	github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
 	github.com/aws/aws-sdk-go-v2 v1.17.7
 	github.com/aws/aws-sdk-go-v2/config v1.18.19
 	github.com/aws/aws-sdk-go-v2/credentials v1.13.18
@@ -35,7 +36,7 @@ require (
 	github.com/hashicorp/go-hclog v1.5.0
 	github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d
 	github.com/hashicorp/go-retryablehttp v0.7.2
-	github.com/jackc/pgx/v5 v5.3.2-0.20230324225134-e9d64ec29d90
+	github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf
 	github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
 	github.com/klauspost/compress v1.16.3
 	github.com/lestrrat-go/jwx/v2 v2.0.9

+ 4 - 2
go.sum

@@ -534,6 +534,8 @@ github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736 h1:qZaEtLxnqY
 github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736/go.mod h1:mTeFRcTdnpzOlRjMoFYC/80HwVUreupyAiqPkCZQOXc=
 github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
 github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=
+github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
+github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -1389,8 +1391,8 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
 github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
 github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
 github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
-github.com/jackc/pgx/v5 v5.3.2-0.20230324225134-e9d64ec29d90 h1:gBugq4KF3zkdaM4oQHSfhUFAfkVQOQpbD20wNggWPP4=
-github.com/jackc/pgx/v5 v5.3.2-0.20230324225134-e9d64ec29d90/go.mod h1:sU+RaYl9qnhD3Ce+mwnFii6YEPx70mCYghBzKvqq4qo=
+github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf h1:BLqUs5GEGMJ+h9s63INbbwXUXe95wvhvh1esISGgau0=
+github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf/go.mod h1:sU+RaYl9qnhD3Ce+mwnFii6YEPx70mCYghBzKvqq4qo=
 github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=

+ 13 - 8
internal/dataprovider/dataprovider.go

@@ -99,6 +99,7 @@ const (
 	md5cryptApr1PwdPrefix     = "$apr1$"
 	sha256cryptPwdPrefix      = "$5$"
 	sha512cryptPwdPrefix      = "$6$"
+	yescryptPwdPrefix         = "$y$"
 	md5LDAPPwdPrefix          = "{MD5}"
 	trackQuotaDisabledError   = "please enable track_quota in your configuration to use this method"
 	operationAdd              = "add"
@@ -165,10 +166,11 @@ var (
 	internalHashPwdPrefixes  = []string{argonPwdPrefix, bcryptPwdPrefix}
 	hashPwdPrefixes          = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
 		pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5LDAPPwdPrefix,
-		sha256cryptPwdPrefix, sha512cryptPwdPrefix}
-	pbkdfPwdPrefixes             = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
-	pbkdfPwdB64SaltPrefixes      = []string{pbkdf2SHA256B64SaltPrefix}
-	unixPwdPrefixes              = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix}
+		sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix}
+	pbkdfPwdPrefixes        = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
+	pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
+	unixPwdPrefixes         = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix,
+		yescryptPwdPrefix}
 	sharedProviders              = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
 	logSender                    = "dataprovider"
 	sqlTableUsers                string
@@ -3081,13 +3083,13 @@ func isPasswordOK(user *User, password string) (bool, error) {
 			return match, err
 		}
 		updatePwd = config.PasswordHashing.Algo != HashingAlgoArgon2ID
-	} else if util.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
-		match, err = comparePbkdf2PasswordAndHash(password, user.Password)
+	} else if util.IsStringPrefixInSlice(user.Password, unixPwdPrefixes) {
+		match, err = compareUnixPasswordAndHash(user, password)
 		if err != nil {
 			return match, err
 		}
-	} else if util.IsStringPrefixInSlice(user.Password, unixPwdPrefixes) {
-		match, err = compareUnixPasswordAndHash(user, password)
+	} else if util.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
+		match, err = comparePbkdf2PasswordAndHash(password, user.Password)
 		if err != nil {
 			return match, err
 		}
@@ -3258,6 +3260,9 @@ func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User, string
 }
 
 func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
+	if strings.HasPrefix(user.Password, yescryptPwdPrefix) {
+		return compareYescryptPassword(user.Password, password)
+	}
 	var crypter crypt.Crypter
 	if strings.HasPrefix(user.Password, sha512cryptPwdPrefix) {
 		crypter = sha512_crypt.New()

+ 39 - 0
internal/dataprovider/unixcrypt.go

@@ -0,0 +1,39 @@
+// Copyright (C) 2019-2023 Nicola Murino
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+//go:build unixcrypt
+// +build unixcrypt
+
+package dataprovider
+
+import (
+	"strings"
+
+	"github.com/amoghe/go-crypt"
+
+	"github.com/drakkan/sftpgo/v2/internal/version"
+)
+
+func init() {
+	version.AddFeature("+unixcrypt")
+}
+
+func compareYescryptPassword(hashedPwd, plainPwd string) (bool, error) {
+	lastIdx := strings.LastIndex(hashedPwd, "$")
+	pwd, err := crypt.Crypt(plainPwd, hashedPwd[:lastIdx+1])
+	if err != nil {
+		return false, err
+	}
+	return pwd == hashedPwd, nil
+}

+ 32 - 0
internal/dataprovider/unixcrypt_disabled.go

@@ -0,0 +1,32 @@
+// Copyright (C) 2019-2023 Nicola Murino
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+//go:build !unixcrypt
+// +build !unixcrypt
+
+package dataprovider
+
+import (
+	"errors"
+
+	"github.com/drakkan/sftpgo/v2/internal/version"
+)
+
+func init() {
+	version.AddFeature("-unixcrypt")
+}
+
+func compareYescryptPassword(_, _ string) (bool, error) {
+	return false, errors.New("yescrypt hash format is not supported or disabled")
+}

+ 1 - 1
openapi/openapi.yaml

@@ -7175,7 +7175,7 @@ components:
           type: array
           items:
             type: string
-          description: 'Features for the current build. Available features are `portable`, `bolt`, `mysql`, `sqlite`, `pgsql`, `s3`, `gcs`, `azblob`, `metrics`. If a feature is available it has a `+` prefix, otherwise a `-` prefix'
+          description: 'Features for the current build. Available features are `portable`, `bolt`, `mysql`, `sqlite`, `pgsql`, `s3`, `gcs`, `azblob`, `metrics`, `unixcrypt`. If a feature is available it has a `+` prefix, otherwise a `-` prefix'
     Token:
       type: object
       properties: