Browse Source

ftpd: use the extra field for certificate authentication

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Nicola Murino 1 year ago
parent
commit
de35eb77cb
4 changed files with 148 additions and 148 deletions
  1. 27 26
      go.mod
  2. 55 53
      go.sum
  3. 52 29
      internal/ftpd/internal_test.go
  4. 14 40
      internal/ftpd/server.go

+ 27 - 26
go.mod

@@ -3,21 +3,21 @@ module github.com/drakkan/sftpgo/v2
 go 1.21
 go 1.21
 
 
 require (
 require (
-	cloud.google.com/go/storage v1.31.0
-	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0
+	cloud.google.com/go/storage v1.32.0
+	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
 	github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
 	github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
 	github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
 	github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
 	github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
 	github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
-	github.com/aws/aws-sdk-go-v2 v1.20.1
-	github.com/aws/aws-sdk-go-v2/config v1.18.33
-	github.com/aws/aws-sdk-go-v2/credentials v1.13.32
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8
-	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.77
-	github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.2
-	github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2
-	github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.0
-	github.com/aws/aws-sdk-go-v2/service/sts v1.21.2
+	github.com/aws/aws-sdk-go-v2 v1.20.2
+	github.com/aws/aws-sdk-go-v2/config v1.18.34
+	github.com/aws/aws-sdk-go-v2/credentials v1.13.33
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.9
+	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.78
+	github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.3
+	github.com/aws/aws-sdk-go-v2/service/s3 v1.38.3
+	github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.1
+	github.com/aws/aws-sdk-go-v2/service/sts v1.21.3
 	github.com/bmatcuk/doublestar/v4 v4.6.0
 	github.com/bmatcuk/doublestar/v4 v4.6.0
 	github.com/cockroachdb/cockroach-go/v2 v2.3.5
 	github.com/cockroachdb/cockroach-go/v2 v2.3.5
 	github.com/coreos/go-oidc/v3 v3.6.0
 	github.com/coreos/go-oidc/v3 v3.6.0
@@ -60,7 +60,7 @@ require (
 	github.com/spf13/viper v1.16.0
 	github.com/spf13/viper v1.16.0
 	github.com/stretchr/testify v1.8.4
 	github.com/stretchr/testify v1.8.4
 	github.com/studio-b12/gowebdav v0.9.0
 	github.com/studio-b12/gowebdav v0.9.0
-	github.com/subosito/gotenv v1.4.2
+	github.com/subosito/gotenv v1.6.0
 	github.com/unrolled/secure v1.13.0
 	github.com/unrolled/secure v1.13.0
 	github.com/wagslane/go-password-validator v0.3.0
 	github.com/wagslane/go-password-validator v0.3.0
 	github.com/wneessen/go-mail v0.4.0
 	github.com/wneessen/go-mail v0.4.0
@@ -74,7 +74,7 @@ require (
 	golang.org/x/sys v0.11.0
 	golang.org/x/sys v0.11.0
 	golang.org/x/term v0.11.0
 	golang.org/x/term v0.11.0
 	golang.org/x/time v0.3.0
 	golang.org/x/time v0.3.0
-	google.golang.org/api v0.136.0
+	google.golang.org/api v0.138.0
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 )
 )
 
 
@@ -86,16 +86,16 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
 	github.com/ajg/form v1.5.1 // indirect
 	github.com/ajg/form v1.5.1 // indirect
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 // indirect
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.39 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.33 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.40 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.2 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.34 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.33 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.13.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.3 // indirect
 	github.com/aws/smithy-go v1.14.1 // indirect
 	github.com/aws/smithy-go v1.14.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/boombuler/barcode v1.0.1 // indirect
 	github.com/boombuler/barcode v1.0.1 // indirect
@@ -108,7 +108,7 @@ require (
 	github.com/fatih/color v1.15.0 // indirect
 	github.com/fatih/color v1.15.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
-	github.com/go-ole/go-ole v1.2.6 // indirect
+	github.com/go-ole/go-ole v1.3.0 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
@@ -162,9 +162,9 @@ require (
 	golang.org/x/tools v0.12.0 // indirect
 	golang.org/x/tools v0.12.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
+	google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
 	google.golang.org/grpc v1.57.0 // indirect
 	google.golang.org/grpc v1.57.0 // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
@@ -172,6 +172,7 @@ require (
 )
 )
 
 
 replace (
 replace (
+	github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20230818123055-c8426c7a1b8d
 	github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
 	github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
 	github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
 	github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
 	golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20230804183749-f40d052136b8
 	golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20230804183749-f40d052136b8

+ 55 - 53
go.sum

@@ -45,12 +45,12 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
-cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0=
+cloud.google.com/go/storage v1.32.0 h1:5w6DxEGOnktmJHarxAOUywxVW9lbNWIzlzzUltG/3+o=
+cloud.google.com/go/storage v1.32.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/OxflYkiD8=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
@@ -72,46 +72,46 @@ github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736/go.mod h1:mTe
 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 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
 github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/aws/aws-sdk-go-v2 v1.20.1 h1:rZBf5DWr7YGrnlTK4kgDQGn1ltqOg5orCYb/UhOFZkg=
-github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
+github.com/aws/aws-sdk-go-v2 v1.20.2 h1:0Aok9u/HVTk7RtY6M1KDcthbaMKGhhS0eLPxIdSIzRI=
+github.com/aws/aws-sdk-go-v2 v1.20.2/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 h1:lN6L3LrYHeZ6xCxaIYtoWCx4GMLk4nRknsh29OMSqHY=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 h1:lN6L3LrYHeZ6xCxaIYtoWCx4GMLk4nRknsh29OMSqHY=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12/go.mod h1:TDCkEAkMTXxTs0oLBGBKpBZbk3NLh8EvAfF0Q3x8/0c=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12/go.mod h1:TDCkEAkMTXxTs0oLBGBKpBZbk3NLh8EvAfF0Q3x8/0c=
-github.com/aws/aws-sdk-go-v2/config v1.18.33 h1:JKcw5SFxFW/rpM4mOPjv0VQ11E2kxW13F3exWOy7VZU=
-github.com/aws/aws-sdk-go-v2/config v1.18.33/go.mod h1:hXO/l9pgY3K5oZJldamP0pbZHdPqqk+4/maa7DSD3cA=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.32 h1:lIH1eKPcCY1ylR4B6PkBGRWMHO3aVenOKJHWiS4/G2w=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.32/go.mod h1:lL8U3v/Y79YRG69WlAho0OHIKUXCyFvSXaIvfo81sls=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 h1:DK/9C+UN/X+1+Wm8pqaDksQr2tSLzq+8X1/rI/ZxKEQ=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8/go.mod h1:ce7BgLQfYr5hQFdy67oX2svto3ufGtm6oBvmsHScI1Q=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.77 h1:oWSNL9oQy+do911sXpJyIc2J7RiUrbm9BecyaGy1wHo=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.77/go.mod h1:xvOdc97VpScJqB10YAI8r/cKuU7d9Ls/as03KROO2qY=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 h1:c8ed/T9T2K5I+h/JzmF5tpI46+OODQ74dzmdo+QnaMg=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38/go.mod h1:qggunOChCMu9ZF/UkAfhTz25+U2rLVb3ya0Ua6TTfCA=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 h1:hNeAAymUY5gu11WrrmFb3CVIp9Dar9hbo44yzzcQpzA=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32/go.mod h1:0ZXSqrty4FtQ7p8TEuRde/SZm9X05KT18LAUlR40Ln0=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 h1:fc0ukRAiP1syoSGZYu+DaE+FulSYhTiJ8WpVu5jElU4=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39/go.mod h1:WLAW8PT7+JhjZfLSWe7WEJaJu0GNo0cKc2Zyo003RBs=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 h1:vUh7dBFNS3oFCtVv6CiYKh5hP9ls8+kIpKLeFruIBLk=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1/go.mod h1:sFMeinkhj/SZKQM8BxtvNtSPjJEo0Xrz+w3g2e4FSKI=
+github.com/aws/aws-sdk-go-v2/config v1.18.34 h1:bFf7CtSgwz/vE4tl0cNbWbf6EDQ2TZR5VrsrO9ardoY=
+github.com/aws/aws-sdk-go-v2/config v1.18.34/go.mod h1:uJ/keVhwR8vsSaErMu2Vb3dArUZZKLVTcOsKXIFfvjs=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.33 h1:esA1X5Eti1xSGCF0W0LYpHH/r6p+MqT0DiKXsfDEPxs=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.33/go.mod h1:jNC10ZEYuLlt9IOowix60yNiO6vGA14RVK3oUfX5KgI=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.9 h1:DnNHcClgyFV5suHJ4axqhmG3YeRGgIu6yv29IEWR9aE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.9/go.mod h1:kz0hzQXlc/5Y5mkbwTKX8A+aTRA45t8Aavly60bQzAQ=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.78 h1:yKlVjl84XK9IshIDplZCUaqwK6jvpQ/h1dQwrzMwZj0=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.78/go.mod h1:IIZC114y2/TZCDCczXrq2bL2nLDUtLqjEFT7zurX8kA=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.39 h1:OBokd2jreL7ItwqRRcN5QiSt24/i2r742aRsd2qMyeg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.39/go.mod h1:OLmjwglQh90dCcFJDGD+T44G0ToLH+696kRwRhS1KOU=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.33 h1:gcRN6PXAo8w3HYFp2wFyr+WYEP4n/a25/IOhzJl36Yw=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.33/go.mod h1:S/zgOphghZAIvrbtvsVycoOncfqh1Hc4uGDIHqDLwTU=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.40 h1:glWaI8WyeYqQN4zh4zqogzSpNPj8rf11Nj+oE3ghQPw=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.40/go.mod h1:OCnFHzgaBY2PuGiHSzLlfqV4j5rJrky7YMfBXcx2Uk0=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.2 h1:9Np6KOCKYnjMwJd1/17ReLdN21gnloI80LNP3uCKk44=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.2/go.mod h1:0YZJZKZCSSbQYQrXpqv0DpIaOMcZ27+OHFaSJTmN+8o=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 h1:iV/W5OMBys+66OeXJi/7xIRrKZNsu0ylsLGu+6nbmQE=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 h1:iV/W5OMBys+66OeXJi/7xIRrKZNsu0ylsLGu+6nbmQE=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13/go.mod h1:ReJb6xYmtGyu9KoFtRreWegbN9dZqvZIIv4vWnhcsyI=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13/go.mod h1:ReJb6xYmtGyu9KoFtRreWegbN9dZqvZIIv4vWnhcsyI=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 h1:QviNkc+vGSuEHx8P+pVNKOdWLXBPIwMFv7p0fphgE4U=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33/go.mod h1:fABTUmOrAgAalG2i9WJpjBvlnk7UK8YmnYaxN+Q2CwE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 h1:dGAseBFEYxth10V23b5e2mAS+tX7oVbfYHD6dnDdAsg=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32/go.mod h1:4jwAWKEkCR0anWk5+1RbfSg1R5Gzld7NLiuaq5bTR/Y=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 h1:PT6PBCycRwhpEW5hJnRiceCeoWJ+r3bdgXtV+VKG7Pk=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1/go.mod h1:TqoxCLwT2nrxrBGA+z7t6OWM7LBkgRckK3gOjYE+7JA=
-github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.2 h1:Sn0OY6ZvpkzDJ84rtcxfjxNhh5A755EwrLvGyNYvrQc=
-github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.2/go.mod h1:Q+KOs5c1mTtEvycj41l1xy9v7QxojZ/c0NhABlYJthY=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2 h1:v346f1h8sUBKXnEbrv43L37MTBlFHyKXQPIZHNAaghA=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2/go.mod h1:cwCATiyNrXK9P2FsWdZ89g9mpsYv2rhk0UA/KByl5fY=
-github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.0 h1:z9faFYBvadv9HdY+oFBgxqCnew9TK+jp9ccxktB5fl4=
-github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.0/go.mod h1:Z6Oq1mXqvgwmUxvMrV/jMkQhwm06A9XO015dzGnS8TM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 h1:A2RlEMo4SJSwbNoUUgkxTAEMduAy/8wG3eB2b2lP4gY=
-github.com/aws/aws-sdk-go-v2/service/sso v1.13.2/go.mod h1:ju+nNXUunfIFamXUIZQiICjnO/TPlOmWcYhZcSy7xaE=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 h1:OJELEgyaT2kmaBGZ+myyZbTTLobfe3ox3FSh5eYK9Qs=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2/go.mod h1:ubDBBaDFs1GHijSOTi8ljppML15GLG0HxhILtbjNNYQ=
-github.com/aws/aws-sdk-go-v2/service/sts v1.21.2 h1:ympg1+Lnq33XLhcK/xTG4yZHPs1Oyxu+6DEWbl7qOzA=
-github.com/aws/aws-sdk-go-v2/service/sts v1.21.2/go.mod h1:FQ/DQcOfESELfJi5ED+IPPAjI5xC6nxtSolVVB773jM=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.34 h1:gQE3p36iC+wwf/hDaCw+tNVXmNxDUehqv5nAvnoG+yc=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.34/go.mod h1:swEfojiNWdgJaOTNT65+XsMclEx4k/tyzBAVEi0Y6vM=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.33 h1:cr70Hw6Lq9cqRst1y4YOHLiaVWaWtBPiqdloinNkfis=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.33/go.mod h1:kcNtzCcEoflp+6e2CDTmm2h3xQGZOBZqYA/8DhYx/S8=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.2 h1:M5vGdcDO+jUGWu7d4BXwcLRXp3UikWXAiCfQI20rqFQ=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.2/go.mod h1:bC2B9AS4ygwMNrefck3XeD6YwXeplWhY6Z2UtlGjv1s=
+github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.3 h1:NnDCBIRlYtreJIuK0PfqpvgPc3aYHpp2dFnxWxAfzUE=
+github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.15.3/go.mod h1:h2345n5Sc82zNIm7XItORbqv44cBx+di6gS7gpZG9OU=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.38.3 h1:yWclTL4cyiqLBWSjxDJ1tjiIzP4x4Kp85aAUtKSbtwA=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.38.3/go.mod h1:yER+u7+gwH6dXy5xRTC2OfoHpYY1BFRiS0SF5iamO6M=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.1 h1:JBrOoTb1gfm4EhlwbMigvLRgOHgouSyQFRbOVQWn3wU=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.1/go.mod h1:fYrcZAwlCzBXN7+5RiJlokZbdIbEsEjwonLyPZQGVGg=
+github.com/aws/aws-sdk-go-v2/service/sso v1.13.3 h1:nceOkYE0jmaG9CoyXHJJm00FAQ8JE+/LCKJJ06hH/Nc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.13.3/go.mod h1:DApEBnZzexe+LDLaNrGOJA8xtRMCpikLW1gX7jZhHxc=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.3 h1:90qW9puxI7LgmiYKSPhx6wz4XqgVauTxCyS3185+JpA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.3/go.mod h1:kKpyLjToIS7E3z0672lBhxIPD+uoQ9V0MYRYCVGIkO0=
+github.com/aws/aws-sdk-go-v2/service/sts v1.21.3 h1:s3wBkMxfA/u2EJJl6KRsPcWv858lDHkhinqXyN6fkZI=
+github.com/aws/aws-sdk-go-v2/service/sts v1.21.3/go.mod h1:b+y9zL57mwCRy6ftp9Nc7CONGHX3sZ50ZCLTrI5xpCc=
 github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs=
 github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs=
 github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
 github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -160,6 +160,8 @@ github.com/drakkan/crypto v0.0.0-20230804183749-f40d052136b8 h1:TUieQf6mz4xlWJav
 github.com/drakkan/crypto v0.0.0-20230804183749-f40d052136b8/go.mod h1:jjOR8ZXZPvxgpYUhVmAtGUCuD1OFc5Hq984QRL686so=
 github.com/drakkan/crypto v0.0.0-20230804183749-f40d052136b8/go.mod h1:jjOR8ZXZPvxgpYUhVmAtGUCuD1OFc5Hq984QRL686so=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
+github.com/drakkan/ftpserverlib v0.0.0-20230818123055-c8426c7a1b8d h1:WcXXDwXoVS85iFI6nCugkkMZqvU9Hb4GAa6MErVlaxY=
+github.com/drakkan/ftpserverlib v0.0.0-20230818123055-c8426c7a1b8d/go.mod h1:dI9/yw/KfJ0g4wmRK8ZukUfqakLr6ZTf9VDydKoLy90=
 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8 h1:tdkLkSKtYd3WSDsZXGJDKsakiNstLQJPN5HjnqCkf2c=
 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8 h1:tdkLkSKtYd3WSDsZXGJDKsakiNstLQJPN5HjnqCkf2c=
 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8/go.mod h1:zOVb1QDhwwqWn2L2qZ0U3swMSO4GTSNyIwXCGO/UGWE=
 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8/go.mod h1:zOVb1QDhwwqWn2L2qZ0U3swMSO4GTSNyIwXCGO/UGWE=
 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
@@ -174,8 +176,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
-github.com/fclairamb/ftpserverlib v0.22.0 h1:PqzyD6YxS5sdb4fAdXUFSODTo8DelsVAOh3LgeR4VXs=
-github.com/fclairamb/ftpserverlib v0.22.0/go.mod h1:dI9/yw/KfJ0g4wmRK8ZukUfqakLr6ZTf9VDydKoLy90=
 github.com/fclairamb/go-log v0.4.1 h1:rLtdSG9x2pK41AIAnE8WYpl05xBJfw1ZyYxZaXFcBsM=
 github.com/fclairamb/go-log v0.4.1 h1:rLtdSG9x2pK41AIAnE8WYpl05xBJfw1ZyYxZaXFcBsM=
 github.com/fclairamb/go-log v0.4.1/go.mod h1:sw1KvnkZ4wKCYkvy4SL3qVZcJSWFP8Ure4pM3z+KNn4=
 github.com/fclairamb/go-log v0.4.1/go.mod h1:sw1KvnkZ4wKCYkvy4SL3qVZcJSWFP8Ure4pM3z+KNn4=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
@@ -200,8 +200,9 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
 github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
 github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
@@ -480,8 +481,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
 github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
 github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
 github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
-github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
-github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
 github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
@@ -668,6 +669,7 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -775,8 +777,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.136.0 h1:e/6enzUE1s4tGPa6Q3ZYShKTtvRc+1Jq0rrafhppmOs=
-google.golang.org/api v0.136.0/go.mod h1:XtJfF+V2zgUxelOn5Zs3kECtluMxneJG8ZxUTlLNTPA=
+google.golang.org/api v0.138.0 h1:K/tVp05MxNVbHShRw9m7e9VJGdagNeTdMzqPH7AUqr0=
+google.golang.org/api v0.138.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -822,12 +824,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 h1:Tyk/35yqszRCvaragTn5NnkY6IiKk/XvHzEWepo71N0=
-google.golang.org/genproto v0.0.0-20230807174057-1744710a1577/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
-google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577 h1:xv8KoglAClYGkprUSmDTKaILtzfD8XzG9NYVXMprjKo=
-google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
+google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 h1:Iveh6tGCJkHAjJgEqUQYGDGgbwmhjoAOz8kO/ajxefY=
+google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
+google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 h1:WGq4lvB/mlicysM/dUT3SBvijH4D3sm/Ny1A4wmt2CI=
+google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

+ 52 - 29
internal/ftpd/internal_test.go

@@ -276,27 +276,28 @@ type mockFTPClientContext struct {
 	lastDataChannel ftpserver.DataChannel
 	lastDataChannel ftpserver.DataChannel
 	remoteIP        string
 	remoteIP        string
 	localIP         string
 	localIP         string
+	extra           any
 }
 }
 
 
-func (cc mockFTPClientContext) Path() string {
+func (cc *mockFTPClientContext) Path() string {
 	return ""
 	return ""
 }
 }
 
 
-func (cc mockFTPClientContext) SetPath(_ string) {}
+func (cc *mockFTPClientContext) SetPath(_ string) {}
 
 
-func (cc mockFTPClientContext) SetListPath(_ string) {}
+func (cc *mockFTPClientContext) SetListPath(_ string) {}
 
 
-func (cc mockFTPClientContext) SetDebug(_ bool) {}
+func (cc *mockFTPClientContext) SetDebug(_ bool) {}
 
 
-func (cc mockFTPClientContext) Debug() bool {
+func (cc *mockFTPClientContext) Debug() bool {
 	return false
 	return false
 }
 }
 
 
-func (cc mockFTPClientContext) ID() uint32 {
+func (cc *mockFTPClientContext) ID() uint32 {
 	return 1
 	return 1
 }
 }
 
 
-func (cc mockFTPClientContext) RemoteAddr() net.Addr {
+func (cc *mockFTPClientContext) RemoteAddr() net.Addr {
 	ip := "127.0.0.1"
 	ip := "127.0.0.1"
 	if cc.remoteIP != "" {
 	if cc.remoteIP != "" {
 		ip = cc.remoteIP
 		ip = cc.remoteIP
@@ -304,7 +305,7 @@ func (cc mockFTPClientContext) RemoteAddr() net.Addr {
 	return &net.IPAddr{IP: net.ParseIP(ip)}
 	return &net.IPAddr{IP: net.ParseIP(ip)}
 }
 }
 
 
-func (cc mockFTPClientContext) LocalAddr() net.Addr {
+func (cc *mockFTPClientContext) LocalAddr() net.Addr {
 	ip := "127.0.0.1"
 	ip := "127.0.0.1"
 	if cc.localIP != "" {
 	if cc.localIP != "" {
 		ip = cc.localIP
 		ip = cc.localIP
@@ -312,34 +313,42 @@ func (cc mockFTPClientContext) LocalAddr() net.Addr {
 	return &net.IPAddr{IP: net.ParseIP(ip)}
 	return &net.IPAddr{IP: net.ParseIP(ip)}
 }
 }
 
 
-func (cc mockFTPClientContext) GetClientVersion() string {
+func (cc *mockFTPClientContext) GetClientVersion() string {
 	return "mock version"
 	return "mock version"
 }
 }
 
 
-func (cc mockFTPClientContext) Close() error {
+func (cc *mockFTPClientContext) Close() error {
 	return nil
 	return nil
 }
 }
 
 
-func (cc mockFTPClientContext) HasTLSForControl() bool {
+func (cc *mockFTPClientContext) HasTLSForControl() bool {
 	return false
 	return false
 }
 }
 
 
-func (cc mockFTPClientContext) HasTLSForTransfers() bool {
+func (cc *mockFTPClientContext) HasTLSForTransfers() bool {
 	return false
 	return false
 }
 }
 
 
-func (cc mockFTPClientContext) SetTLSRequirement(_ ftpserver.TLSRequirement) error {
+func (cc *mockFTPClientContext) SetTLSRequirement(_ ftpserver.TLSRequirement) error {
 	return nil
 	return nil
 }
 }
 
 
-func (cc mockFTPClientContext) GetLastCommand() string {
+func (cc *mockFTPClientContext) GetLastCommand() string {
 	return ""
 	return ""
 }
 }
 
 
-func (cc mockFTPClientContext) GetLastDataChannel() ftpserver.DataChannel {
+func (cc *mockFTPClientContext) GetLastDataChannel() ftpserver.DataChannel {
 	return cc.lastDataChannel
 	return cc.lastDataChannel
 }
 }
 
 
+func (cc *mockFTPClientContext) SetExtra(extra any) {
+	cc.extra = extra
+}
+
+func (cc *mockFTPClientContext) Extra() any {
+	return cc.extra
+}
+
 // MockOsFs mockable OsFs
 // MockOsFs mockable OsFs
 type MockOsFs struct {
 type MockOsFs struct {
 	vfs.Fs
 	vfs.Fs
@@ -566,7 +575,7 @@ func TestUserInvalidParams(t *testing.T) {
 		},
 		},
 	}
 	}
 	server := NewServer(c, configDir, binding, 3)
 	server := NewServer(c, configDir, binding, 3)
-	_, err := server.validateUser(u, mockFTPClientContext{}, dataprovider.LoginMethodPassword)
+	_, err := server.validateUser(u, &mockFTPClientContext{}, dataprovider.LoginMethodPassword)
 	assert.Error(t, err)
 	assert.Error(t, err)
 
 
 	u.Username = "a"
 	u.Username = "a"
@@ -588,10 +597,10 @@ func TestUserInvalidParams(t *testing.T) {
 		},
 		},
 		VirtualPath: vdirPath2,
 		VirtualPath: vdirPath2,
 	})
 	})
-	_, err = server.validateUser(u, mockFTPClientContext{}, dataprovider.LoginMethodPassword)
+	_, err = server.validateUser(u, &mockFTPClientContext{}, dataprovider.LoginMethodPassword)
 	assert.Error(t, err)
 	assert.Error(t, err)
 	u.VirtualFolders = nil
 	u.VirtualFolders = nil
-	_, err = server.validateUser(u, mockFTPClientContext{}, dataprovider.LoginMethodPassword)
+	_, err = server.validateUser(u, &mockFTPClientContext{}, dataprovider.LoginMethodPassword)
 	assert.Error(t, err)
 	assert.Error(t, err)
 }
 }
 
 
@@ -600,16 +609,16 @@ func TestFTPMode(t *testing.T) {
 		BaseConnection: common.NewBaseConnection("", common.ProtocolFTP, "", "", dataprovider.User{}),
 		BaseConnection: common.NewBaseConnection("", common.ProtocolFTP, "", "", dataprovider.User{}),
 	}
 	}
 	assert.Empty(t, connection.getFTPMode())
 	assert.Empty(t, connection.getFTPMode())
-	connection.clientContext = mockFTPClientContext{lastDataChannel: ftpserver.DataChannelActive}
+	connection.clientContext = &mockFTPClientContext{lastDataChannel: ftpserver.DataChannelActive}
 	assert.Equal(t, "active", connection.getFTPMode())
 	assert.Equal(t, "active", connection.getFTPMode())
-	connection.clientContext = mockFTPClientContext{lastDataChannel: ftpserver.DataChannelPassive}
+	connection.clientContext = &mockFTPClientContext{lastDataChannel: ftpserver.DataChannelPassive}
 	assert.Equal(t, "passive", connection.getFTPMode())
 	assert.Equal(t, "passive", connection.getFTPMode())
-	connection.clientContext = mockFTPClientContext{lastDataChannel: 0}
+	connection.clientContext = &mockFTPClientContext{lastDataChannel: 0}
 	assert.Empty(t, connection.getFTPMode())
 	assert.Empty(t, connection.getFTPMode())
 }
 }
 
 
 func TestClientVersion(t *testing.T) {
 func TestClientVersion(t *testing.T) {
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("2_%v", mockCC.ID())
 	connID := fmt.Sprintf("2_%v", mockCC.ID())
 	user := dataprovider.User{}
 	user := dataprovider.User{}
 	connection := &Connection{
 	connection := &Connection{
@@ -627,7 +636,7 @@ func TestClientVersion(t *testing.T) {
 }
 }
 
 
 func TestDriverMethodsNotImplemented(t *testing.T) {
 func TestDriverMethodsNotImplemented(t *testing.T) {
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("2_%v", mockCC.ID())
 	connID := fmt.Sprintf("2_%v", mockCC.ID())
 	user := dataprovider.User{}
 	user := dataprovider.User{}
 	connection := &Connection{
 	connection := &Connection{
@@ -647,6 +656,20 @@ func TestDriverMethodsNotImplemented(t *testing.T) {
 	assert.Equal(t, connection.GetID(), connection.Name())
 	assert.Equal(t, connection.GetID(), connection.Name())
 }
 }
 
 
+func TestExtraData(t *testing.T) {
+	mockCC := mockFTPClientContext{}
+	_, ok := mockCC.Extra().(bool)
+	require.False(t, ok)
+	mockCC.SetExtra(false)
+	val, ok := mockCC.Extra().(bool)
+	require.True(t, ok)
+	require.False(t, val)
+	mockCC.SetExtra(true)
+	val, ok = mockCC.Extra().(bool)
+	require.True(t, ok)
+	require.True(t, val)
+}
+
 func TestResolvePathErrors(t *testing.T) {
 func TestResolvePathErrors(t *testing.T) {
 	user := dataprovider.User{
 	user := dataprovider.User{
 		BaseUser: sdk.BaseUser{
 		BaseUser: sdk.BaseUser{
@@ -655,7 +678,7 @@ func TestResolvePathErrors(t *testing.T) {
 	}
 	}
 	user.Permissions = make(map[string][]string)
 	user.Permissions = make(map[string][]string)
 	user.Permissions["/"] = []string{dataprovider.PermAny}
 	user.Permissions["/"] = []string{dataprovider.PermAny}
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connection := &Connection{
 	connection := &Connection{
 		BaseConnection: common.NewBaseConnection(connID, common.ProtocolFTP, "", "", user),
 		BaseConnection: common.NewBaseConnection(connID, common.ProtocolFTP, "", "", user),
@@ -717,7 +740,7 @@ func TestUploadFileStatError(t *testing.T) {
 	}
 	}
 	user.Permissions = make(map[string][]string)
 	user.Permissions = make(map[string][]string)
 	user.Permissions["/"] = []string{dataprovider.PermAny}
 	user.Permissions["/"] = []string{dataprovider.PermAny}
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	fs := vfs.NewOsFs(connID, user.HomeDir, "", nil)
 	fs := vfs.NewOsFs(connID, user.HomeDir, "", nil)
 	connection := &Connection{
 	connection := &Connection{
@@ -748,7 +771,7 @@ func TestAVBLErrors(t *testing.T) {
 	}
 	}
 	user.Permissions = make(map[string][]string)
 	user.Permissions = make(map[string][]string)
 	user.Permissions["/"] = []string{dataprovider.PermAny}
 	user.Permissions["/"] = []string{dataprovider.PermAny}
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connection := &Connection{
 	connection := &Connection{
 		BaseConnection: common.NewBaseConnection(connID, common.ProtocolFTP, "", "", user),
 		BaseConnection: common.NewBaseConnection(connID, common.ProtocolFTP, "", "", user),
@@ -770,7 +793,7 @@ func TestUploadOverwriteErrors(t *testing.T) {
 	}
 	}
 	user.Permissions = make(map[string][]string)
 	user.Permissions = make(map[string][]string)
 	user.Permissions["/"] = []string{dataprovider.PermAny}
 	user.Permissions["/"] = []string{dataprovider.PermAny}
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	fs := newMockOsFs(nil, nil, false, connID, user.GetHomeDir())
 	fs := newMockOsFs(nil, nil, false, connID, user.GetHomeDir())
 	connection := &Connection{
 	connection := &Connection{
@@ -826,7 +849,7 @@ func TestTransferErrors(t *testing.T) {
 	}
 	}
 	user.Permissions = make(map[string][]string)
 	user.Permissions = make(map[string][]string)
 	user.Permissions["/"] = []string{dataprovider.PermAny}
 	user.Permissions["/"] = []string{dataprovider.PermAny}
-	mockCC := mockFTPClientContext{}
+	mockCC := &mockFTPClientContext{}
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	connID := fmt.Sprintf("%v", mockCC.ID())
 	fs := newMockOsFs(nil, nil, false, connID, user.GetHomeDir())
 	fs := newMockOsFs(nil, nil, false, connID, user.GetHomeDir())
 	connection := &Connection{
 	connection := &Connection{
@@ -1015,7 +1038,7 @@ func TestPassiveIPResolver(t *testing.T) {
 	ip = net.ParseIP("192.168.0.2")
 	ip = net.ParseIP("192.168.0.2")
 	assert.False(t, b.PassiveIPOverrides[0].parsedNetworks[0](ip))
 	assert.False(t, b.PassiveIPOverrides[0].parsedNetworks[0](ip))
 
 
-	mockCC := mockFTPClientContext{
+	mockCC := &mockFTPClientContext{
 		remoteIP: "192.168.1.10",
 		remoteIP: "192.168.1.10",
 		localIP:  "192.168.1.3",
 		localIP:  "192.168.1.3",
 	}
 	}

+ 14 - 40
internal/ftpd/server.go

@@ -22,7 +22,6 @@ import (
 	"net"
 	"net"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
-	"sync"
 
 
 	ftpserver "github.com/fclairamb/ftpserverlib"
 	ftpserver "github.com/fclairamb/ftpserverlib"
 	"github.com/sftpgo/sdk/plugin/notifier"
 	"github.com/sftpgo/sdk/plugin/notifier"
@@ -38,26 +37,23 @@ import (
 
 
 // Server implements the ftpserverlib MainDriver interface
 // Server implements the ftpserverlib MainDriver interface
 type Server struct {
 type Server struct {
-	ID               int
-	config           *Configuration
-	initialMsg       string
-	statusBanner     string
-	binding          Binding
-	tlsConfig        *tls.Config
-	mu               sync.RWMutex
-	verifiedTLSConns map[uint32]bool
+	ID           int
+	config       *Configuration
+	initialMsg   string
+	statusBanner string
+	binding      Binding
+	tlsConfig    *tls.Config
 }
 }
 
 
 // NewServer returns a new FTP server driver
 // NewServer returns a new FTP server driver
 func NewServer(config *Configuration, configDir string, binding Binding, id int) *Server {
 func NewServer(config *Configuration, configDir string, binding Binding, id int) *Server {
 	binding.setCiphers()
 	binding.setCiphers()
 	server := &Server{
 	server := &Server{
-		config:           config,
-		initialMsg:       config.Banner,
-		statusBanner:     fmt.Sprintf("SFTPGo %v FTP Server", version.Get().Version),
-		binding:          binding,
-		ID:               id,
-		verifiedTLSConns: make(map[uint32]bool),
+		config:       config,
+		initialMsg:   config.Banner,
+		statusBanner: fmt.Sprintf("SFTPGo %v FTP Server", version.Get().Version),
+		binding:      binding,
+		ID:           id,
 	}
 	}
 	if config.BannerFile != "" {
 	if config.BannerFile != "" {
 		bannerFilePath := config.BannerFile
 		bannerFilePath := config.BannerFile
@@ -76,27 +72,6 @@ func NewServer(config *Configuration, configDir string, binding Binding, id int)
 	return server
 	return server
 }
 }
 
 
-func (s *Server) isTLSConnVerified(id uint32) bool {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	return s.verifiedTLSConns[id]
-}
-
-func (s *Server) setTLSConnVerified(id uint32, value bool) {
-	s.mu.Lock()
-	defer s.mu.Unlock()
-
-	s.verifiedTLSConns[id] = value
-}
-
-func (s *Server) cleanTLSConnVerification(id uint32) {
-	s.mu.Lock()
-	defer s.mu.Unlock()
-
-	delete(s.verifiedTLSConns, id)
-}
-
 // GetSettings returns FTP server settings
 // GetSettings returns FTP server settings
 func (s *Server) GetSettings() (*ftpserver.Settings, error) {
 func (s *Server) GetSettings() (*ftpserver.Settings, error) {
 	if err := s.binding.checkPassiveIP(); err != nil {
 	if err := s.binding.checkPassiveIP(); err != nil {
@@ -190,7 +165,6 @@ func (s *Server) ClientConnected(cc ftpserver.ClientContext) (string, error) {
 
 
 // ClientDisconnected is called when the user disconnects, even if he never authenticated
 // ClientDisconnected is called when the user disconnects, even if he never authenticated
 func (s *Server) ClientDisconnected(cc ftpserver.ClientContext) {
 func (s *Server) ClientDisconnected(cc ftpserver.ClientContext) {
-	s.cleanTLSConnVerification(cc.ID())
 	connID := fmt.Sprintf("%v_%v_%v", common.ProtocolFTP, s.ID, cc.ID())
 	connID := fmt.Sprintf("%v_%v_%v", common.ProtocolFTP, s.ID, cc.ID())
 	common.Connections.Remove(connID)
 	common.Connections.Remove(connID)
 	common.Connections.RemoveClientConnection(util.GetIPFromRemoteAddress(cc.RemoteAddr().String()))
 	common.Connections.RemoveClientConnection(util.GetIPFromRemoteAddress(cc.RemoteAddr().String()))
@@ -199,7 +173,7 @@ func (s *Server) ClientDisconnected(cc ftpserver.ClientContext) {
 // AuthUser authenticates the user and selects an handling driver
 // AuthUser authenticates the user and selects an handling driver
 func (s *Server) AuthUser(cc ftpserver.ClientContext, username, password string) (ftpserver.ClientDriver, error) {
 func (s *Server) AuthUser(cc ftpserver.ClientContext, username, password string) (ftpserver.ClientDriver, error) {
 	loginMethod := dataprovider.LoginMethodPassword
 	loginMethod := dataprovider.LoginMethodPassword
-	if s.isTLSConnVerified(cc.ID()) {
+	if verified, ok := cc.Extra().(bool); ok && verified {
 		loginMethod = dataprovider.LoginMethodTLSCertificateAndPwd
 		loginMethod = dataprovider.LoginMethodTLSCertificateAndPwd
 	}
 	}
 	ipAddr := util.GetIPFromRemoteAddress(cc.RemoteAddr().String())
 	ipAddr := util.GetIPFromRemoteAddress(cc.RemoteAddr().String())
@@ -255,7 +229,7 @@ func (s *Server) VerifyConnection(cc ftpserver.ClientContext, user string, tlsCo
 	if !s.binding.isMutualTLSEnabled() {
 	if !s.binding.isMutualTLSEnabled() {
 		return nil, nil
 		return nil, nil
 	}
 	}
-	s.setTLSConnVerified(cc.ID(), false)
+	cc.SetExtra(false)
 	if tlsConn != nil {
 	if tlsConn != nil {
 		state := tlsConn.ConnectionState()
 		state := tlsConn.ConnectionState()
 		if len(state.PeerCertificates) > 0 {
 		if len(state.PeerCertificates) > 0 {
@@ -272,7 +246,7 @@ func (s *Server) VerifyConnection(cc ftpserver.ClientContext, user string, tlsCo
 					return nil, err
 					return nil, err
 				}
 				}
 
 
-				s.setTLSConnVerified(cc.ID(), true)
+				cc.SetExtra(true)
 
 
 				if dbUser.IsLoginMethodAllowed(dataprovider.LoginMethodTLSCertificate, common.ProtocolFTP) {
 				if dbUser.IsLoginMethodAllowed(dataprovider.LoginMethodTLSCertificate, common.ProtocolFTP) {
 					connection, err := s.validateUser(dbUser, cc, dataprovider.LoginMethodTLSCertificate)
 					connection, err := s.validateUser(dbUser, cc, dataprovider.LoginMethodTLSCertificate)