Skip to content

Commit

Permalink
cmd/tailscale, util/quarantine: set quarantine flags on files from Ta…
Browse files Browse the repository at this point in the history
…ildrop

This sets the "com.apple.quarantine" flag on macOS, and the
"Zone.Identifier" alternate data stream on Windows.

Change-Id: If14f805467b0e2963067937d7f34e08ba1d1fa85
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
(cherry picked from commit 0af61f7)
  • Loading branch information
andrew-d authored and DentonGentry committed Nov 18, 2022
1 parent f99a3e5 commit 6dbfafd
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 0 deletions.
5 changes: 5 additions & 0 deletions cmd/tailscale/cli/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/util/quarantine"
"tailscale.com/version"
)

Expand Down Expand Up @@ -393,6 +394,10 @@ func receiveFile(ctx context.Context, wf apitype.WaitingFile, dir string) (targe
if err != nil {
return "", 0, err
}
// Apply quarantine attribute before copying
if err := quarantine.SetOnFile(f); err != nil {
return "", 0, fmt.Errorf("failed to apply quarantine attribute to file %v: %v", f.Name(), err)
}
_, err = io.Copy(f, rc)
if err != nil {
f.Close()
Expand Down
3 changes: 3 additions & 0 deletions cmd/tailscale/depaware.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
D github.com/google/uuid from tailscale.com/util/quarantine
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
Expand Down Expand Up @@ -102,6 +103,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/util/lineread from tailscale.com/net/interfaces+
tailscale.com/util/mak from tailscale.com/net/netcheck
tailscale.com/util/multierr from tailscale.com/control/controlhttp
tailscale.com/util/quarantine from tailscale.com/cmd/tailscale/cli
tailscale.com/util/singleflight from tailscale.com/net/dnscache
L tailscale.com/util/strs from tailscale.com/hostinfo
W 💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
Expand Down Expand Up @@ -170,6 +172,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
crypto/tls from github.com/tcnksm/go-httpstat+
crypto/x509 from crypto/tls+
crypto/x509/pkix from crypto/x509+
D database/sql/driver from github.com/google/uuid
embed from tailscale.com/cmd/tailscale/cli+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
Expand Down
15 changes: 15 additions & 0 deletions util/quarantine/quarantine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package quarantine sets platform specific "quarantine" attributes on files
// that are received from other hosts.
package quarantine

import "os"

// SetOnFile sets the platform-specific quarantine attribute (if any) on the
// provided file.
func SetOnFile(f *os.File) error {
return setQuarantineAttr(f)
}
57 changes: 57 additions & 0 deletions util/quarantine/quarantine_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package quarantine

import (
"fmt"
"os"
"strings"
"time"

"github.com/google/uuid"
"golang.org/x/sys/unix"
)

func setQuarantineAttr(f *os.File) error {
sc, err := f.SyscallConn()
if err != nil {
return err
}

now := time.Now()

// We uppercase the UUID to match what other applications on macOS do
id := strings.ToUpper(uuid.New().String())

// kLSQuarantineTypeOtherDownload; this matches what AirDrop sets when
// receiving a file.
quarantineType := "0001"

// This format is under-documented, but the following links contain a
// reasonably comprehensive overview:
// https://eclecticlight.co/2020/10/29/quarantine-and-the-quarantine-flag/
// https://nixhacker.com/security-protection-in-macos-1/
// https://ilostmynotes.blogspot.com/2012/06/gatekeeper-xprotect-and-quarantine.html
attrData := fmt.Sprintf("%s;%x;%s;%s",
quarantineType, // quarantine value
now.Unix(), // time in hex
"Tailscale", // application
id, // UUID
)

var innerErr error
err = sc.Control(func(fd uintptr) {
innerErr = unix.Fsetxattr(
int(fd),
"com.apple.quarantine", // attr
[]byte(attrData),
0,
)
})
if err != nil {
return err
}
return innerErr
}
15 changes: 15 additions & 0 deletions util/quarantine/quarantine_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build !darwin && !windows

package quarantine

import (
"os"
)

func setQuarantineAttr(f *os.File) error {
return nil
}
30 changes: 30 additions & 0 deletions util/quarantine/quarantine_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package quarantine

import (
"os"
"strings"
)

func setQuarantineAttr(f *os.File) error {
// Documentation on this can be found here:
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
//
// Additional information can be found at:
// https://www.digital-detective.net/forensic-analysis-of-zone-identifier-stream/
// https://bugzilla.mozilla.org/show_bug.cgi?id=1433179
content := strings.Join([]string{
"[ZoneTransfer]",

// "URLZONE_INTERNET"
// https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms537175(v=vs.85)
"ZoneId=3",

// TODO(andrew): should/could we add ReferrerUrl or HostUrl?
}, "\r\n")

return os.WriteFile(f.Name()+":Zone.Identifier", []byte(content), 0)
}

0 comments on commit 6dbfafd

Please sign in to comment.