summaryrefslogtreecommitdiffhomepage
path: root/ipn
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2021-04-16 10:57:46 -0700
committerBrad Fitzpatrick <bradfitz@tailscale.com>2021-04-16 10:58:19 -0700
commitb993d9802a2a59fc8b97ad4e783e8c331df5fe60 (patch)
treed3a1151569fc0efed973a1c51d86bd70d0cd5d73 /ipn
parent2f422434aa6f89d9c4e3437ce50dda78a9e680b5 (diff)
downloadtailscale-b993d9802a2a59fc8b97ad4e783e8c331df5fe60.tar.xz
tailscale-b993d9802a2a59fc8b97ad4e783e8c331df5fe60.zip
ipn/ipnlocal, etc: require file sharing capability to send/recv files
tailscale/corp#1582 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Diffstat (limited to 'ipn')
-rw-r--r--ipn/ipnlocal/local.go24
-rw-r--r--ipn/ipnlocal/peerapi.go4
-rw-r--r--ipn/ipnstate/ipnstate.go3
-rw-r--r--ipn/localapi/localapi.go15
4 files changed, 44 insertions, 2 deletions
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index 51761bc19..18d8b310f 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -2215,12 +2215,36 @@ func (b *LocalBackend) OpenFile(name string) (rc io.ReadCloser, size int64, err
return apiSrv.OpenFile(name)
}
+// hasCapFileSharing reports whether the current node has the file
+// sharing capability enabled.
+func (b *LocalBackend) hasCapFileSharing() bool {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ return b.hasCapFileSharingLocked()
+}
+
+func (b *LocalBackend) hasCapFileSharingLocked() bool {
+ nm := b.netMap
+ if nm == nil || nm.SelfNode == nil {
+ return false
+ }
+ for _, c := range nm.SelfNode.Capabilities {
+ if c == tailcfg.CapabilityFileSharing {
+ return true
+ }
+ }
+ return false
+}
+
// FileTargets lists nodes that the current node can send files to.
func (b *LocalBackend) FileTargets() ([]*apitype.FileTarget, error) {
var ret []*apitype.FileTarget
b.mu.Lock()
defer b.mu.Unlock()
+ if !b.hasCapFileSharingLocked() {
+ return nil, errors.New("file sharing not enabled by Tailscale admin")
+ }
nm := b.netMap
if b.state != ipn.Running || nm == nil {
return nil, errors.New("not connected")
diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go
index 2af91f234..b1a43b3c2 100644
--- a/ipn/ipnlocal/peerapi.go
+++ b/ipn/ipnlocal/peerapi.go
@@ -410,6 +410,10 @@ func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not owner", http.StatusForbidden)
return
}
+ if !h.ps.b.hasCapFileSharing() {
+ http.Error(w, "file sharing not enabled by Tailscale admin", http.StatusForbidden)
+ return
+ }
if r.Method != "PUT" {
http.Error(w, "not method PUT", http.StatusMethodNotAllowed)
return
diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go
index a748f3271..cbc3413ca 100644
--- a/ipn/ipnstate/ipnstate.go
+++ b/ipn/ipnstate/ipnstate.go
@@ -88,7 +88,8 @@ type PeerStatus struct {
KeepAlive bool
ExitNode bool // true if this is the currently selected exit node.
- PeerAPIURL []string
+ PeerAPIURL []string
+ Capabilities []string `json:",omitempty"`
// ShareeNode indicates this node exists in the netmap because
// it's owned by a shared-to user and that node might connect
diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go
index 8e57f85e1..f8adb188b 100644
--- a/ipn/localapi/localapi.go
+++ b/ipn/localapi/localapi.go
@@ -9,6 +9,7 @@ import (
"crypto/rand"
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"io"
"net"
@@ -299,6 +300,18 @@ func (h *Handler) serveFiles(w http.ResponseWriter, r *http.Request) {
io.Copy(w, rc)
}
+func writeErrorJSON(w http.ResponseWriter, err error) {
+ if err == nil {
+ err = errors.New("unexpected nil error")
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(500)
+ type E struct {
+ Error string `json:"error"`
+ }
+ json.NewEncoder(w).Encode(E{err.Error()})
+}
+
func (h *Handler) serveFileTargets(w http.ResponseWriter, r *http.Request) {
if !h.PermitRead {
http.Error(w, "access denied", http.StatusForbidden)
@@ -310,7 +323,7 @@ func (h *Handler) serveFileTargets(w http.ResponseWriter, r *http.Request) {
}
fts, err := h.b.FileTargets()
if err != nil {
- http.Error(w, err.Error(), 500)
+ writeErrorJSON(w, err)
return
}
makeNonNil(&fts)