-
Notifications
You must be signed in to change notification settings - Fork 601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
snap-confine: bind mount /usr/lib/snapd relative to snap-confine #3956
Conversation
Use the basedir of snap-confine to bind mount as /usr/lib/snapd when using bases. This ensures we use the re-execed tools if we are re-execing. This fixes the bug that busybox-static does not work with the "bare" snap when using a snapd that re-execs from e.g. beta.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I think we can remove one of the calls to sc_must_snprintf
but it is just an optimization. Technically dirname
can return the .
directory but that would require the attacker to be able to spoof /proc/self/exe already. I don't think we need more defense there. CC @jdstrand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the improvement :)
cmd/snap-confine/mount-support.c
Outdated
char self[PATH_MAX + 1] = { 0, }; | ||
if (readlink("/proc/self/exe", self, sizeof self) > 0) { | ||
char *dir = dirname(self); | ||
sc_must_snprintf(src, sizeof src, "%s", dir); | ||
src = dirname(self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments inline. I think in a follow-up PR it would make sense to look for other instances of readlink() and dirname() in snap-confine and make sure we are being consistent. A small refactor to call a helper function might be in order.
cmd/snap-confine/mount-support.c
Outdated
// that we are re-execing from | ||
char *src = LIBEXECDIR; | ||
char self[PATH_MAX + 1] = { 0, }; | ||
if (readlink("/proc/self/exe", self, sizeof self) > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use sizeof(self) - 1
here? readlink() doesn't append a NULL byte so if for some reason the kernel returned too much, we are safe (since you initialized 'self' to all NULLs). That seems to be your intent for using PATH_MAX + 1, but let's not trust there are no kernel bugs either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might also point out that the not all /proc/*/exes are symlinks, but they will be for /proc/self/exe for snap-confine (ie, kernel processes won't be a symlink). If we are guarding against kernel bugs, we could:
if (strstr(self, "/snap-confine") == NULL) {
die(...);
}
This is a harder kernel bug to imagine happening, but it's a cheap check. @zyga, I didn't think of this one in the 3621 review. Follow-up PR would be fine there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdstrand ack, will do.
cmd/snap-confine/mount-support.c
Outdated
char self[PATH_MAX + 1] = { 0, }; | ||
if (readlink("/proc/self/exe", self, sizeof self) > 0) { | ||
src = dirname(self); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should } else { die(...) }
. no? If the kernel is failing to give what we need, we probably don't want to continue using whatever LIBEXECDIR is set to, but maybe I am missing something? Not to mention, we are initializing src to LIBEXECDIR, but then we immediately reassign it to dirname so, barring a kernel bug, we are using extra memory on the stack for no reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was initially thinking that LIBEXECDIR is a valid fallback if for whatever reason we cannot read /proc - however if you think we should fail hard in this case I'm happy to add code for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code to die() if /proc/self/exe cannot be read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make the most sense to use char *src = NULL;
and require readlink() to succeed and when it does, src = dirname(self)
.
cmd/snap-confine/mount-support.c
Outdated
char *src = LIBEXECDIR; | ||
char self[PATH_MAX + 1] = { 0, }; | ||
if (readlink("/proc/self/exe", self, sizeof self) > 0) { | ||
src = dirname(self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are guarding against kernel bugs, and we know that dirname will always return a NULL-terminated string, we can do a cheap check on the first character (we are doing this in PR 3621's bootstrap.c, for example). We should probably check for other occurrences in the snap-confine sources):
// dirname(path) might return '.' depending on path. /proc/self/exe should always point
// to an absolute path, but let's guarantee that.
if (src[0] != '/') {
die(...)
}
cmd/snap-confine/mount-support.c
Outdated
@@ -363,9 +363,19 @@ static void sc_bootstrap_mount_namespace(const struct sc_mount_config *config) | |||
// where $ROOT is either "/" or the "/snap/core/current" | |||
// that we are re-execing from | |||
char *src = LIBEXECDIR; | |||
char self[PATH_MAX + 1] = { 0, }; | |||
if (readlink("/proc/self/exe", self, sizeof self) > 0) { | |||
char self[PATH_MAX] = { 0, }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note I didn't mean for you to remove PATH_MAX+1 here. Your change for sizeof(self) -1
guards against the issue I was referring to (so this code is safe), but now it means it will fail in the unlikely event that a valid path has length of PATH_MAX.
cmd/snap-confine/mount-support.c
Outdated
} | ||
src = dirname(self); | ||
// dirname(path) might return '.' depending on path. /proc/self/exe should always point | ||
// to an absolute path, but let's guarantee that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think you need a go fmt here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates! Approving since the code here is hardened and robust, but please consider my follow on comment regarding PATH_MAX+1.
Thanks! Sorry that I misunderstood the earlier comment. This is fixed now. |
@mvo5 the test failures look real. I think some apparmor tweak is needed for snap-confine's profile. |
When app snap uses a custom base snap we bind-mount the aforementioned directory from the core snap but that bind mount can be safely made to be read-only. Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
Codecov Report
@@ Coverage Diff @@
## master #3956 +/- ##
==========================================
+ Coverage 75.93% 75.96% +0.03%
==========================================
Files 420 423 +3
Lines 36310 36525 +215
==========================================
+ Hits 27571 27746 +175
- Misses 6807 6838 +31
- Partials 1932 1941 +9
Continue to review full report at Codecov.
|
* snap-confine: bind mount /usr/lib/snapd relative to snap-confine Use the basedir of snap-confine to bind mount as /usr/lib/snapd when using bases. This ensures we use the re-execed tools if we are re-execing. This fixes the bug that busybox-static does not work with the "bare" snap when using a snapd that re-execs from e.g. beta.
Use the basedir of snap-confine to bind mount as /usr/lib/snapd
when using bases. This ensures we use the re-execed tools if we
are re-execing. This fixes the bug that busybox-static does not
work with the "bare" snap when using a snapd that re-execs from
e.g. beta.