Mailinglist Archive: opensuse-kernel (95 mails)

< Previous Next >
Re: [opensuse-kernel] Re: openSUSE Kernel: Push Patches Upstream
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 04/29/2011 11:36 AM, Greg KH wrote:
On Fri, Apr 29, 2011 at 08:38:44AM +0200, Marcus Meissner wrote:
On Thu, Apr 28, 2011 at 07:40:08PM -0400, Jeff Mahoney wrote:
On 04/28/2011 02:50 PM, Brandon Philips wrote:
On 13:35 Thu 28 Apr 2011, Jeff Mahoney wrote:
- - patches.suse/file-capabilities-disable-by-default.diff
[never submitted]

We should drop it. Upstream has had file caps by default since Nov 2009.

Upstream commits:
b3a222e52e4d4be77cc4520a57af1a4a0d8222d1
1f29fae29709b4668979e244c09b2fa78ff1ad59

In doing a zypper dup today, I saw that something is looking at
/proc/cmdline for "file_caps" as part of SuSEconfig running. We should
probably figure out what that is.

The SuSEconfig permissions module, it decides on whether to handle file caps
setting or not depending on the kernel.

Ludwig wants a patch to detect this for years in the mainline kernel, but it
is
not there yet.

Can you please take care that such a method is exposed by mainline?
(file_caps might be disabled or enabled, we should handle both cases and
detect
it from the kernel.)


The patch doing this in the kernel is queued up to be accepted for the
.40 kernel release, so perhaps I should also add it to our tree now so
that FACTORY can be converted over to using it and this can be dropped?

If so, just let me know and I will go add it as it is in my upstream
kernel tree already.

And it was my fault that it took so long to get accepted for the past 6+
months, sorry, it got lost in my patch queue :(

Just noticed I did "reply list" instead of "reply all" for my original
reply to this. Here we go again, with a more fleshed out attachment.

This has now become interesting, it seems. I just noticed that /bin/ping
has capabilities set and is no longer suid root on two of my factory
machines. Consequently, it's not working unless I boot with file_caps on
the command line.

Attached is a program that will report whether the kernel is honoring
file capabilities on the file system where the program is located. It
should be run as root. It will set the CAP_NET_RAW capability on itself,
drop privileges, and fork/exec itself to compare how the capabilities
have changed. Root will always return with all capabilities set, but the
user's capability set will change based on the file capabilities.

- -Jeff

- --
Jeff Mahoney
SUSE Labs
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org/

iEYEARECAAYFAk3dS4sACgkQLPWxlyuTD7J26gCeNkMvp0W3r9lBSRYue26wqdA3
5CMAnRfaROaRSyIxF5XM/pzbc9M2aGq0
=mp5A
-----END PGP SIGNATURE-----
/*
* Copyright (C) 2011 Jeff Mahoney <jeffm@xxxxxxxx>, SUSE Linux
*
* When run as root, this program will set the CAP_NET_RAW file capability
* on itself, drop priviledges, and then fork/exec to test whether the
* capability is honored by another user.
*
* It must be located on a file system where file capabilities are supported.
*
*/

#include <sys/capability.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <pwd.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <stdarg.h>

static void
error(const char *fmt, ...)
{
va_list args;
char path[PATH_MAX];
int ret;
va_start(args, fmt);

ret = readlink("/proc/self/exe", path, sizeof(path));
if (ret < 0) {
va_end(args);
fprintf(stderr, "error while dumping error. strange.\n");
exit(1);
}

fprintf(stderr, "%s: ", basename(path));
vfprintf(stderr, fmt, args);

va_end(args);
}

static int
add_cap(const char *path)
{
int ret;
cap_t cap;

cap = cap_from_text("cap_net_raw+ep");
if (!cap) {
error("cap_from_text failed: %s\n", strerror(errno));
return -1;
}

ret = cap_set_file(path, cap);
if (ret) {
error("cap_set_file failed: %s\n", strerror(errno));
}
cap_free(cap);
return ret;
}

static int
has_cap(const char *path)
{
int ret = -1;
cap_t cap, fcap;
cap_flag_value_t val;

fcap = cap_get_file(path);
if (!fcap) {
if (errno == ENODATA)
error("Capability isn't set for file; Can't test.\n");
else
error("cap_get_file failed: %s\n", strerror(errno));
return -1;
}

ret = cap_get_flag(fcap, CAP_NET_RAW, CAP_PERMITTED, &val);
cap_free(fcap);
if (ret) {
error("cap_get_flag failed: %s\n", strerror(errno));
return -1;
} else if (val != 1) {
error("Capability isn't set for file; Can't test.\n");
return -1;
}

cap = cap_get_proc();
if (!cap) {
error ("cap_get_proc failed: %s\n", strerror(errno));
return -1;
}
ret = cap_get_flag(cap, CAP_NET_RAW, CAP_EFFECTIVE, &val);
if (ret)
error("cap_get_flag failed: %s\n", strerror(errno));
else
ret = val;
cap_free(cap);

return ret;
}

int has_cap_unpriv(int uid, const char *path)
{

int ret = -1;
int pid;
int status;

pid = fork();
switch (pid) {
case 0: {
cap_t cap;
close(STDOUT_FILENO);

ret = setuid(uid);
if (ret) {
error("setuid(%d) failed: %s\n", uid, strerror(errno));
return -1;
}

seteuid(uid);
if (ret) {
error("seteuid(%d) failed: %s\n", uid, strerror(errno));
return -1;
}

/* Inherit nothing, must be after setuid */
cap = cap_init();
if (!cap) {
error("cap_init failed: %s\n", strerror(errno));
return -1;
}
ret = cap_set_proc(cap);
if (ret) {
error("cap_set_proc failed: %s\n", strerror(errno));
}
cap_free(cap);

execl(path, path, "-u", NULL);
error("exec failed: %s\n", strerror(errno));
return -1;
}
case -1:
error("fork failed: %s\n", strerror(errno));
break;
default:
ret = waitpid(pid, &status, 0);
if (ret > 0) {
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 1)
return 0;
else if (WEXITSTATUS(status) == 2)
return 1;
}
}
return -1;
}
return ret;
}

int main(int argc, char *argv[])
{
struct passwd *pwd;
int ret;
char path[PATH_MAX];
int euid = geteuid();

ret = readlink("/proc/self/exe", path, sizeof(path));
if (ret < 0) {
fprintf(stderr, "could not read /proc/self/exe. failing\n");
return 1;
}

if (euid == 0) {
int uid = 65534;
int root_has_cap, user_has_cap;

ret = add_cap(path);
if (ret)
return ret;

root_has_cap = has_cap(path);
if (root_has_cap == -1)
return -1;

pwd = getpwnam("nobody");
if (pwd)
uid = pwd->pw_uid;

user_has_cap = has_cap_unpriv(uid, path);
if (user_has_cap == -1)
return -1;

if (root_has_cap != user_has_cap || !root_has_cap) {
printf("file_caps: ignored\n");
return 1;
} else {
printf("file_caps: honored\n");
return 0;
}
} else {
int x = getopt(argc, argv, "u");
if (x != 'u') {
error("This program must be run as root for "
"results to mean anything.\n");
return -1;
}

ret = has_cap(path);
if (ret == 0)
return 1;
else if (ret == 1)
return 2;
return -1;
}

return 0;
}
< Previous Next >
Follow Ups