Skip to content

Commit f82c4ff

Browse files
committed
PoC for a shell interpreter sandbox.
This uses mvdan/sh/interp to provide a builtin POSIX shell interpreter whose file operations are (mostly) restricted to a sandbox root. I say mostly because the builtin shell globbing (eg. `echo /etc/*`) still allows listing directories outside the sandbox, though accessing them is prevented. The basics seem to work though there would be quite a bit more work fleshing out the supported utilities. Security wise I'm not sure this gives us much, as one of the goals of this is to allow executables _within_ the sandbox to be executed (eg. Java's keytool). If this is allowed then the package can basically execute arbitrary code without restriction, so I don't think there are many/any security benefits whatsoever. However there are other benefits: consistent shell support across any OS, including Windows. There is no need to rely on particular versions of bash being present. Some safety guarantees around accidentally violating the sandbox - eg. a script that accidentally rm's some files. Are these benefits large enough to warrant fleshing this out? I am not certain.
1 parent 20bc42b commit f82c4ff

File tree

10 files changed

+374
-5
lines changed

10 files changed

+374
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.henv
2+
.hermit
23
testdata/env
34
build
45
pkgs

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ require (
1717
github.com/gobwas/glob v0.2.3
1818
github.com/gofrs/flock v0.8.0
1919
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
20-
github.com/kr/text v0.2.0 // indirect
2120
github.com/mattn/go-isatty v0.0.12
2221
github.com/mitchellh/go-ps v1.0.0
2322
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
@@ -33,8 +32,9 @@ require (
3332
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
3433
go.etcd.io/bbolt v1.3.5
3534
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
36-
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
35+
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324
3736
golang.org/x/text v0.3.2 // indirect
3837
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
3938
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5
39+
mvdan.cc/sh/v3 v3.3.0
4040
)

go.sum

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270/go.mod h1:2Xt
2222
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
2323
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
2424
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
25+
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
26+
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2527
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2628
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2729
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -31,6 +33,7 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
3133
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
3234
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
3335
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
36+
github.com/google/renameio v1.0.1-0.20210406141108-81588dbe0453/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
3437
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
3538
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
3639
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -39,6 +42,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
3942
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
4043
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
4144
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
45+
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
46+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
4247
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
4348
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
4449
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -49,13 +54,15 @@ github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc
4954
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
5055
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
5156
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
57+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
5258
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
5359
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
5460
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
5561
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5662
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5763
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc=
5864
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4=
65+
github.com/rogpeppe/go-internal v1.7.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
5966
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda h1:h+YpzUB/bGVJcLqW+d5GghcCmE/A25KbzjXvWJQi/+o=
6067
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda/go.mod h1:MSotTrCv1PwoR8QgU1JurEx+lNNbtr25I+m0zbLyAGw=
6168
github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f h1:PF9WV5j/x6MT+x/sauUHd4objCvJbZb0wdxZkHSdd5A=
@@ -90,14 +97,18 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
9097
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
9198
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
9299
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
100+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
101+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
93102
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
94103
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
95104
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
96105
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
97-
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
98-
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
99-
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
106+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
107+
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324 h1:pAwJxDByZctfPwzlNGrDN2BQLsdPb9NkhoTJtUkAO28=
108+
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
100109
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
110+
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
111+
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
101112
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
102113
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
103114
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -110,9 +121,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
110121
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
111122
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
112123
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
124+
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
113125
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
114126
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
115127
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
116128
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
117129
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc=
118130
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
131+
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
132+
mvdan.cc/sh/v3 v3.3.0 h1:ujzElMnry63f4I5sjPFxzo6xia+gwsHZM0yyauuyZ6k=
133+
mvdan.cc/sh/v3 v3.3.0/go.mod h1:dh3avhLDhJJ/MJKzbak6FYn+DJKUWk7Fb6Dh5mGdv6Y=

shell/sandbox/builtins.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package interp
2+
3+
import (
4+
"path/filepath"
5+
"strings"
6+
7+
"github.com/alecthomas/kong"
8+
"github.com/pkg/errors"
9+
"mvdan.cc/sh/v3/interp"
10+
)
11+
12+
var builtins = map[string]func() builtinCmd{
13+
"ls": func() builtinCmd { return &lsCmd{} },
14+
"mkdir": func() builtinCmd { return &mkdirCmd{} },
15+
"rm": func() builtinCmd { return &rmCmd{} },
16+
}
17+
18+
type cmdCtx struct {
19+
*Sandbox
20+
interp.HandlerContext
21+
runner *interp.Runner
22+
}
23+
24+
// Sanitise a path within the sandbox.
25+
func (c *cmdCtx) Sanitise(path string) (string, error) {
26+
if !filepath.IsAbs(path) {
27+
path = filepath.Join(c.Dir, path)
28+
}
29+
if !strings.HasPrefix(path, c.root) {
30+
return "", errors.Wrap(ErrSandboxViolation, path)
31+
}
32+
return path, nil
33+
}
34+
35+
type builtinCmd interface {
36+
Run(bctx cmdCtx) error
37+
}
38+
39+
func runBuiltinCmd(bctx cmdCtx, args []string) (error, bool) {
40+
factory, ok := builtins[args[0]]
41+
if !ok {
42+
return nil, false
43+
}
44+
cmd := factory()
45+
_, err := kong.Must(
46+
cmd,
47+
kong.Exit(func(i int) {}),
48+
kong.Description("List files."),
49+
).Parse(args[1:])
50+
if err != nil {
51+
return errors.Wrap(err, args[0]), true
52+
}
53+
return errors.Wrap(cmd.Run(bctx), args[0]), true
54+
}

shell/sandbox/find.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package interp
2+
3+
type findCmd struct {
4+
}

shell/sandbox/ls.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package interp
2+
3+
import (
4+
"fmt"
5+
"io/fs"
6+
"io/ioutil"
7+
"os"
8+
"os/user"
9+
"path/filepath"
10+
"strings"
11+
"syscall"
12+
"text/tabwriter"
13+
14+
"github.com/pkg/errors"
15+
)
16+
17+
type lsCmd struct {
18+
All bool `short:"a" help:"List all files."`
19+
Long bool `short:"l" help:"Long listing."`
20+
21+
Paths []string `arg:"" optional:"" help:"Paths to list."`
22+
}
23+
24+
func (l *lsCmd) Run(ctx cmdCtx) error {
25+
paths := l.Paths
26+
if len(paths) == 0 {
27+
paths = append(paths, ctx.Dir)
28+
}
29+
w := tabwriter.NewWriter(ctx.Stdout, 4, 4, 1, ' ', 0)
30+
for _, path := range paths {
31+
path, err := ctx.Sanitise(path)
32+
if err != nil {
33+
return err
34+
}
35+
info, err := os.Stat(path)
36+
if err != nil {
37+
return errors.WithStack(err)
38+
}
39+
var entries []fs.FileInfo
40+
if info.IsDir() {
41+
entries, err = ioutil.ReadDir(path)
42+
if err != nil {
43+
return errors.WithStack(err)
44+
}
45+
// Synthesise . and ..
46+
if l.All {
47+
dotdot, err := os.Stat(filepath.Join(path, ".."))
48+
if err != nil {
49+
return errors.WithStack(err)
50+
}
51+
entries = append([]fs.FileInfo{
52+
&renamedFileInfo{info, "."},
53+
&renamedFileInfo{dotdot, ".."},
54+
}, entries...)
55+
}
56+
} else {
57+
entries = append(entries, info)
58+
}
59+
for _, entry := range entries {
60+
if !l.All && strings.HasPrefix(entry.Name(), ".") {
61+
continue
62+
}
63+
if !l.Long {
64+
fmt.Fprintln(w, entry.Name())
65+
continue
66+
}
67+
var (
68+
size uint16
69+
username = "user"
70+
groupname = "group"
71+
)
72+
if stat, ok := entry.Sys().(*syscall.Stat_t); ok {
73+
usr, err := user.LookupId(fmt.Sprintf("%d", stat.Uid))
74+
if err != nil {
75+
return errors.WithStack(err)
76+
}
77+
group, err := user.LookupGroupId(fmt.Sprintf("%d", stat.Gid))
78+
if err != nil {
79+
return errors.WithStack(err)
80+
}
81+
username = usr.Username
82+
groupname = group.Name
83+
size = stat.Nlink
84+
85+
}
86+
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%d\t%s\t%s\n",
87+
entry.Mode(), size, username, groupname, entry.Size(),
88+
entry.ModTime().Format("2 Jan 15:04"),
89+
entry.Name())
90+
}
91+
}
92+
return w.Flush()
93+
}
94+
95+
type renamedFileInfo struct {
96+
fs.FileInfo
97+
name string
98+
}
99+
100+
func (r *renamedFileInfo) Name() string {
101+
return r.name
102+
}

shell/sandbox/mkdir.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package interp
2+
3+
import (
4+
"os"
5+
6+
"github.com/pkg/errors"
7+
)
8+
9+
type mkdirCmd struct {
10+
Mode os.FileMode `short:"m" help:"Set the file permission bits of the final created directory to the specified mode."`
11+
Intermediate bool `short:"p" help:"Create intermediate directories as needed."`
12+
Dirs []string `arg:"" help:"Directories to create"`
13+
}
14+
15+
func (m *mkdirCmd) Run(ctx cmdCtx) error {
16+
mode := m.Mode
17+
if mode == 0 {
18+
mode = 0777
19+
}
20+
for _, dir := range m.Dirs {
21+
dir, err := ctx.Sanitise(dir)
22+
if err != nil {
23+
return err
24+
}
25+
if m.Intermediate {
26+
if err := os.MkdirAll(dir, mode); err != nil {
27+
return errors.Wrap(err, dir)
28+
}
29+
} else {
30+
if err := os.Mkdir(dir, mode); err != nil {
31+
return errors.Wrap(err, dir)
32+
}
33+
}
34+
}
35+
return nil
36+
}

shell/sandbox/rm.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package interp
2+
3+
import (
4+
"os"
5+
6+
"github.com/pkg/errors"
7+
)
8+
9+
type rmCmd struct {
10+
Recursive bool `short:"r" help:"Recursively delete."`
11+
Force bool `short:"f" help:"Force deletion of read-only files."`
12+
13+
Paths []string `arg:"" help:"Paths to delete."`
14+
}
15+
16+
func (r *rmCmd) Run(bctx cmdCtx) error {
17+
for _, path := range r.Paths {
18+
path, err := bctx.Sanitise(path)
19+
if err != nil {
20+
return err
21+
}
22+
if r.Recursive {
23+
if err = os.RemoveAll(path); err != nil {
24+
return errors.WithStack(err)
25+
}
26+
} else {
27+
if err = os.Remove(path); err != nil {
28+
return errors.WithStack(err)
29+
}
30+
}
31+
}
32+
return nil
33+
}

0 commit comments

Comments
 (0)