Skip to content

Commit

Permalink
Support snapctl from snap apps, not just hooks.
Browse files Browse the repository at this point in the history
  • Loading branch information
stolowski committed May 17, 2017
1 parent 5d4551d commit 16a0a92
Show file tree
Hide file tree
Showing 28 changed files with 466 additions and 46 deletions.
2 changes: 2 additions & 0 deletions cmd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ snap_confine_snap_confine_SOURCES = \
snap-confine/snap-confine-args.c \
snap-confine/snap-confine-args.h \
snap-confine/snap-confine.c \
snap-confine/context-support.c \
snap-confine/context-support.h \
snap-confine/udev-support.c \
snap-confine/udev-support.h \
snap-confine/user-support.c \
Expand Down
28 changes: 28 additions & 0 deletions cmd/libsnap-confine-private/snap.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ bool verify_security_tag(const char *security_tag)
return (status == 0);
}

bool sc_verify_hook_security_tag_name(const char *security_tag)
{
const char *whitelist_re =
"^snap\\.[a-z](-?[a-z0-9])*\\.(hook\\.[a-z](-?[a-z])*)$";

regex_t re;
if (regcomp(&re, whitelist_re, REG_EXTENDED | REG_NOSUB) != 0)
die("can not compile regex %s", whitelist_re);

int status = regexec(&re, security_tag, 0, NULL, 0);
regfree(&re);

return (status == 0);
}

bool verify_snap_name(const char *name)
{
const char *whitelist_re = "[a-z](-?[a-z0-9])*$";
regex_t re;
if (regcomp(&re, whitelist_re, REG_EXTENDED | REG_NOSUB) != 0)
die("can not compile regex %s", whitelist_re);

int status = regexec(&re, name, 0, NULL, 0);
regfree(&re);

return (status == 0);
}

static int skip_lowercase_letters(const char **p)
{
int skipped = 0;
Expand Down
3 changes: 3 additions & 0 deletions cmd/libsnap-confine-private/snap.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ void sc_snap_name_validate(const char *snap_name, struct sc_error **errorp);

bool verify_security_tag(const char *security_tag);

bool sc_verify_hook_security_tag_name(const char *security_tag);
bool verify_snap_name(const char *name);

#endif
83 changes: 83 additions & 0 deletions cmd/snap-confine/context-support.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "context-support.h"

#include "../libsnap-confine-private/cleanup-funcs.h"
#include "../libsnap-confine-private/string-utils.h"
#include "../libsnap-confine-private/utils.h"

#include "config.h"

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define CONTEXT_DIR "/var/lib/snapd/context"

char *sc_nonfatal_context_get_from_snapd(const char *snap_name,
struct sc_error **errorp)
{
char context_path[PATH_MAX];
char *context_val = NULL;
struct sc_error *err = NULL;

if (snap_name == NULL) {
die("SNAP_NAME is not set");
}

sc_must_snprintf(context_path, sizeof(context_path), "%s/snap.%s",
CONTEXT_DIR, snap_name);
int fd __attribute__ ((cleanup(sc_cleanup_close))) = -1;
fd = open(context_path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0) {
err =
sc_error_init(SC_ERRNO_DOMAIN, 0,
"cannot open context file %s, SNAP_CONTEXT will not be set: %s",
context_path, strerror(errno));
goto out;
}
// context is a 32 bytes, base64-encoding makes it 44.
context_val = calloc(1, 45);
if (context_val == NULL) {
die("failed to allocate memory for snap context");
}
if (read(fd, context_val, 44) < 0) {
free(context_val);
context_val = NULL;
err =
sc_error_init(SC_ERRNO_DOMAIN, 0,
"failed to read context file %s: %s",
context_path, strerror(errno));
goto out;
}

out:
sc_error_forward(errorp, err);
return context_val;
}

void sc_maybe_set_context_environment(const char *context)
{
if (context != NULL) {
// Overwrite context env value.
setenv("SNAP_CONTEXT", context, 1);
}
}
42 changes: 42 additions & 0 deletions cmd/snap-confine/context-support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#ifndef SNAP_CONFINE_CONTEXT_SUPPORT_H
#define SNAP_CONFINE_CONTEXT_SUPPORT_H

#include "../libsnap-confine-private/error.h"

/**
* Return snap context string for given snap.
*
* The context value is read from /var/lib/snapd/contexts/snap.<snapname>
* file. The caller of the function takes the ownership of the returned context
* string.
* If the file cannot be read then an error is returned in errorp and
* the function returns NULL.
**/
char *sc_nonfatal_context_get_from_snapd(const char *snap_name,
struct sc_error **errorp);

/**
* Set the snap context environment variable.
*
* Set the SNAP_CONTEXT environment variable with the value of context.
**/
void sc_maybe_set_context_environment(const char *context);

#endif
3 changes: 3 additions & 0 deletions cmd/snap-confine/snap-confine.apparmor.in
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@
# Allow snap-confine to move to the void
/var/lib/snapd/void/ r,

# Allow snap-confine to read snap contexts
/var/lib/snapd/contexts/snap.* r,

# Support for the quirk system
/var/ r,
/var/lib/ r,
Expand Down
25 changes: 20 additions & 5 deletions cmd/snap-confine/snap-confine.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@
#include "mount-support.h"
#include "ns-support.h"
#include "quirks.h"
#ifdef HAVE_SECCOMP
#include "seccomp-support.h"
#endif // ifdef HAVE_SECCOMP
#include "udev-support.h"
#include "user-support.h"
#include "context-support.h"
#include "snap-confine-args.h"
#ifdef HAVE_SECCOMP
#include "seccomp-support.h"
#endif // ifdef HAVE_SECCOMP

int main(int argc, char **argv)
{
Expand Down Expand Up @@ -84,6 +85,20 @@ int main(int argc, char **argv)
die("need to run as root or suid");
}
#endif

char *snap_context __attribute__ ((cleanup(sc_cleanup_string))) = NULL;
// Do no get snap context value if running a hook (we don't want to overwrite hook's SNAP_CONTEXT)
if (!sc_verify_hook_security_tag_name(security_tag)) {
struct sc_error *err
__attribute__ ((cleanup(sc_cleanup_error))) = NULL;
snap_context =
sc_nonfatal_context_get_from_snapd(snap_name, &err);
if (err != NULL) {
error("cannot get context: %s",
sc_error_msg(err));
}
}

struct sc_apparmor apparmor;
sc_init_apparmor_support(&apparmor);
if (!apparmor.is_confined && apparmor.mode != SC_AA_NOT_APPLICABLE
Expand Down Expand Up @@ -201,8 +216,8 @@ int main(int argc, char **argv)
#if 0
setup_user_xdg_runtime_dir();
#endif

// https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement
sc_maybe_set_context_environment(snap_context);
// https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement
sc_maybe_aa_change_onexec(&apparmor, security_tag);
#ifdef HAVE_SECCOMP
sc_load_seccomp_context(seccomp_ctx);
Expand Down
13 changes: 12 additions & 1 deletion daemon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2284,7 +2284,10 @@ func runSnapctl(c *Command, r *http.Request, user *auth.UserState) Response {

// Right now snapctl is only used for hooks. If at some point it grows
// beyond that, this probably shouldn't go straight to the HookManager.
context, _ := c.d.overlord.HookManager().Context(snapctlOptions.ContextID)
context, err := c.d.overlord.HookManager().Context(snapctlOptions.ContextID)
if err != nil {
return BadRequest("error running snapctl: %s", err)
}
stdout, stderr, err := ctlcmd.Run(context, snapctlOptions.Args)
if err != nil {
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
Expand All @@ -2294,6 +2297,14 @@ func runSnapctl(c *Command, r *http.Request, user *auth.UserState) Response {
}
}

if context.IsEphemeral() {
context.Lock()
defer context.Unlock()
if err := context.Done(); err != nil {
return BadRequest(i18n.G("set failed: %v"), err)
}
}

result := map[string]string{
"stdout": string(stdout),
"stderr": string(stderr),
Expand Down
2 changes: 2 additions & 0 deletions dirs/dirs.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
SnapDeviceDir string

SnapAssertsDBDir string
SnapContextDir string
SnapTrustedAccountKey string
SnapAssertsSpoolDir string

Expand Down Expand Up @@ -151,6 +152,7 @@ func SetRootDir(rootdir string) {
SnapSocket = filepath.Join(rootdir, "/run/snapd-snap.socket")

SnapAssertsDBDir = filepath.Join(rootdir, snappyDir, "assertions")
SnapContextDir = filepath.Join(rootdir, snappyDir, "context")
SnapAssertsSpoolDir = filepath.Join(rootdir, "run/snapd/auto-import")

SnapStateFile = filepath.Join(rootdir, snappyDir, "state.json")
Expand Down
2 changes: 1 addition & 1 deletion overlord/configstate/configstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (s *tasksetsSuite) TestConfigure(c *C) {
c.Assert(hooksup.IgnoreError, Equals, test.ignoreError)
c.Assert(hooksup.Timeout, Equals, 5*time.Minute)

context, err := hookstate.NewContext(task, &hooksup, nil)
context, err := hookstate.NewContext(task, task.State(), &hooksup, nil, "")
c.Check(err, IsNil)
c.Check(context.SnapName(), Equals, "test-snap")
c.Check(context.SnapRevision(), Equals, snap.Revision{})
Expand Down
2 changes: 1 addition & 1 deletion overlord/configstate/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (s *configureHandlerSuite) SetUpTest(c *C) {
setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"}

var err error
s.context, err = hookstate.NewContext(task, setup, hooktest.NewMockHandler())
s.context, err = hookstate.NewContext(task, task.State(), setup, hooktest.NewMockHandler(), "")
c.Assert(err, IsNil)

s.handler = configstate.NewConfigureHandler(s.context)
Expand Down
Loading

0 comments on commit 16a0a92

Please sign in to comment.