diff --git a/go.mod b/go.mod index 9def7be7..631b4975 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.19 github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.1 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2 github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 github.com/cockroachdb/cockroach-go/v2 v2.2.16 github.com/coreos/go-oidc/v3 v3.4.0 @@ -109,7 +109,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.5.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -156,7 +156,7 @@ require ( golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220927151529-dcaddaf36704 // indirect + google.golang.org/genproto v0.0.0-20220929141241-1ce7b20da813 // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -167,6 +167,7 @@ require ( replace ( github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 + github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7 golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220925175854-fd2128ac8ea8 golang.org/x/net => github.com/drakkan/net v0.0.0-20220925175748-018cd5f6a745 ) diff --git a/go.sum b/go.sum index a4aac07a..96baa224 100644 --- a/go.sum +++ b/go.sum @@ -190,8 +190,8 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw= github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.1 h1:eMsEmvJR6zQ1lDi59RDtCc62x9fKs1kv2b8A8nPpWmY= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.1/go.mod h1:HEBBc70BYi5eUvxBqC3xXjU/04NO96X/XNUe5qhC7Bc= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2 h1:3x1Qilin49XQ1rK6pDNAfG+DmCFPfB7Rrpl+FUDAR/0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2/go.mod h1:HEBBc70BYi5eUvxBqC3xXjU/04NO96X/XNUe5qhC7Bc= github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw= github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM= github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI+N/H1I1ynHwXnO6FowXJc0= @@ -270,6 +270,8 @@ github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHP github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= github.com/drakkan/net v0.0.0-20220925175748-018cd5f6a745 h1:Lgtp3izEEJFPxMLJfLWxc1htWNMZhK7EkBo0/dmEX4E= github.com/drakkan/net v0.0.0-20220925175748-018cd5f6a745/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7 h1:Hj7AAfZ5yt9QuCxSQDllRygmL33xJ2sZLOmcyyOAdYU= +github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4= github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -441,8 +443,9 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -665,9 +668,6 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pkg/sftp v1.13.6-0.20220831160757-628507938ec6 h1:hxLT9qX4jw+GjGuPA6XHtooT1+nf/hr5anQtACaXZmY= -github.com/pkg/sftp v1.13.6-0.20220831160757-628507938ec6/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= @@ -945,7 +945,6 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1230,8 +1229,8 @@ google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220927151529-dcaddaf36704 h1:H1AcWFV69NFCMeBJ8nVLtv8uHZZ5Ozcgoq012hHEFuU= -google.golang.org/genproto v0.0.0-20220927151529-dcaddaf36704/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220929141241-1ce7b20da813 h1:buul04Ikd79A5tP8nGhKEyMfr+/HplsO6nqSUapWZ/M= +google.golang.org/genproto v0.0.0-20220929141241-1ce7b20da813/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= 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.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/internal/common/connection.go b/internal/common/connection.go index 41610780..5e3495cd 100644 --- a/internal/common/connection.go +++ b/internal/common/connection.go @@ -546,6 +546,13 @@ func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath string) err // CreateSymlink creates fsTargetPath as a symbolic link to fsSourcePath func (c *BaseConnection) CreateSymlink(virtualSourcePath, virtualTargetPath string) error { + var relativePath string + if !path.IsAbs(virtualSourcePath) { + relativePath = virtualSourcePath + virtualSourcePath = path.Join(path.Dir(virtualTargetPath), relativePath) + c.Log(logger.LevelDebug, "link relative path %q resolved as %q, target path %q", + relativePath, virtualSourcePath, virtualTargetPath) + } if c.isCrossFoldersRequest(virtualSourcePath, virtualTargetPath) { c.Log(logger.LevelWarn, "cross folder symlink is not supported, src: %v dst: %v", virtualSourcePath, virtualTargetPath) return c.GetOpUnsupportedError() @@ -579,6 +586,9 @@ func (c *BaseConnection) CreateSymlink(virtualSourcePath, virtualTargetPath stri c.Log(logger.LevelError, "symlink target path %#v is not allowed", virtualTargetPath) return c.GetPermissionDeniedError() } + if relativePath != "" { + fsSourcePath = relativePath + } if err := fs.Symlink(fsSourcePath, fsTargetPath); err != nil { c.Log(logger.LevelError, "failed to create symlink %#v -> %#v: %+v", fsSourcePath, fsTargetPath, err) return c.GetFsError(fs, err) diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go index 1236446e..90eb3fc7 100644 --- a/internal/common/protocol_test.go +++ b/internal/common/protocol_test.go @@ -313,6 +313,71 @@ func TestBaseConnection(t *testing.T) { assert.NoError(t, err) } +func TestRelativeSymlinks(t *testing.T) { + u := getTestUser() + user, _, err := httpdtest.AddUser(u, http.StatusCreated) + assert.NoError(t, err) + err = os.RemoveAll(user.GetHomeDir()) + assert.NoError(t, err) + conn, client, err := getSftpClient(user) + if assert.NoError(t, err) { + defer conn.Close() + defer client.Close() + + linkName := testFileName + "_link" + err = client.Symlink("non-existent-file", linkName) + assert.NoError(t, err) + err = client.Remove(linkName) + assert.NoError(t, err) + testDir := "sub" + err = client.Mkdir(testDir) + assert.NoError(t, err) + f, err := client.Create(path.Join(testDir, testFileName)) + assert.NoError(t, err) + _, err = f.Write(testFileContent) + assert.NoError(t, err) + err = f.Close() + assert.NoError(t, err) + err = client.Symlink(path.Join(testDir, testFileName), linkName) + assert.NoError(t, err) + _, err = client.Stat(linkName) + assert.NoError(t, err) + p, err := client.ReadLink(linkName) + assert.NoError(t, err) + assert.Equal(t, path.Join("/", testDir, testFileName), p) + err = client.Remove(linkName) + assert.NoError(t, err) + + err = client.Symlink(testFileName, path.Join(testDir, linkName)) + assert.NoError(t, err) + _, err = client.Stat(path.Join(testDir, linkName)) + assert.NoError(t, err) + p, err = client.ReadLink(path.Join(testDir, linkName)) + assert.NoError(t, err) + assert.Equal(t, path.Join("/", testDir, testFileName), p) + + f, err = client.Create(testFileName) + assert.NoError(t, err) + _, err = f.Write(testFileContent) + assert.NoError(t, err) + err = f.Close() + assert.NoError(t, err) + + err = client.Symlink(testFileName, linkName) + assert.NoError(t, err) + _, err = client.Stat(linkName) + assert.NoError(t, err) + p, err = client.ReadLink(linkName) + assert.NoError(t, err) + assert.Equal(t, path.Join("/", testFileName), p) + } + + _, err = httpdtest.RemoveUser(user, http.StatusOK) + assert.NoError(t, err) + err = os.RemoveAll(user.GetHomeDir()) + assert.NoError(t, err) +} + func TestCheckFsAfterUpdate(t *testing.T) { u := getTestUser() user, _, err := httpdtest.AddUser(u, http.StatusCreated) @@ -2264,19 +2329,19 @@ func TestVirtualFoldersLink(t *testing.T) { assert.NoError(t, err) err = client.Symlink(path.Join(vdirPath2, testFileName), path.Join(vdirPath2, testDir, testFileName+".link")) assert.NoError(t, err) - err = client.Symlink(testFileName, path.Join(vdirPath1, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath1, testFileName+".link1")) if assert.Error(t, err) { assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") } - err = client.Symlink(testFileName, path.Join(vdirPath1, testDir, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath1, testDir, testFileName+".link1")) if assert.Error(t, err) { assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") } - err = client.Symlink(testFileName, path.Join(vdirPath2, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath2, testFileName+".link1")) if assert.Error(t, err) { assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") } - err = client.Symlink(testFileName, path.Join(vdirPath2, testDir, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath2, testDir, testFileName+".link1")) if assert.Error(t, err) { assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") } diff --git a/internal/sftpd/sftpd_test.go b/internal/sftpd/sftpd_test.go index 1b811232..b7c57751 100644 --- a/internal/sftpd/sftpd_test.go +++ b/internal/sftpd/sftpd_test.go @@ -607,7 +607,7 @@ func TestBasicSFTPFsHandling(t *testing.T) { err = client.Mkdir(linkDir) assert.NoError(t, err) linkToLinkPath := path.Join(linkDir, testLinkToLinkName) - err = client.Symlink(testLinkName, linkToLinkPath) + err = client.Symlink(path.Join("/", testLinkName), linkToLinkPath) assert.NoError(t, err) info, err = client.Lstat(linkToLinkPath) if assert.NoError(t, err) { @@ -733,6 +733,8 @@ func TestSFTPFsEscapeHomeDir(t *testing.T) { // linkName points to a link inside the home dir and this link points to a dir outside the home dir _, err = client.ReadLink(linkName) assert.ErrorIs(t, err, os.ErrPermission) + _, err = client.RealPath(linkName) + assert.ErrorIs(t, err, os.ErrPermission) _, err = client.ReadDir(linkName) assert.ErrorIs(t, err, os.ErrPermission) _, err = client.ReadDir(path.Join(dirName, linkName)) @@ -1233,7 +1235,7 @@ func TestRealPath(t *testing.T) { err = client.Mkdir(subdir) assert.NoError(t, err) linkName := testFileName + "_link" - err = client.Symlink(testFileName, path.Join(subdir, linkName)) + err = client.Symlink(path.Join("/", testFileName), path.Join(subdir, linkName)) assert.NoError(t, err) p, err = client.RealPath(path.Join(subdir, linkName)) assert.NoError(t, err) @@ -6890,13 +6892,13 @@ func TestVirtualFoldersLink(t *testing.T) { assert.NoError(t, err) err = client.Symlink(path.Join(vdirPath2, testFileName), path.Join(vdirPath2, testDir, testFileName+".link")) assert.NoError(t, err) - err = client.Symlink(testFileName, path.Join(vdirPath1, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath1, testFileName+".link1")) assert.Error(t, err) - err = client.Symlink(testFileName, path.Join(vdirPath1, testDir, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath1, testDir, testFileName+".link1")) assert.Error(t, err) - err = client.Symlink(testFileName, path.Join(vdirPath2, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath2, testFileName+".link1")) assert.Error(t, err) - err = client.Symlink(testFileName, path.Join(vdirPath2, testDir, testFileName+".link1")) + err = client.Symlink(path.Join("/", testFileName), path.Join(vdirPath2, testDir, testFileName+".link1")) assert.Error(t, err) err = client.Symlink(path.Join(vdirPath1, testFileName), testFileName+".link1") assert.Error(t, err) @@ -7355,7 +7357,7 @@ func TestPermList(t *testing.T) { } err = client.Mkdir("sub") assert.NoError(t, err) - err = client.Symlink(testFileName, path.Join("/sub", testFileName)) + err = client.Symlink(path.Join("/", testFileName), path.Join("/sub", testFileName)) assert.NoError(t, err) _, err = client.ReadLink(path.Join("/sub", testFileName)) assert.Error(t, err, "read remote link without permission on targe dir should not succeed") diff --git a/pkgs/build.sh b/pkgs/build.sh index 44ca30c7..5ef09081 100755 --- a/pkgs/build.sh +++ b/pkgs/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -NFPM_VERSION=2.19.1 +NFPM_VERSION=2.19.2 NFPM_ARCH=${NFPM_ARCH:-amd64} if [ -z ${SFTPGO_VERSION} ] then