apparmor/module-deprecated/procattr.c

333 lines
6.9 KiB
C

/*
* Copyright (C) 2005 Novell/SUSE
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*
* AppArmor /proc/pid/attr handling
*/
/* for isspace */
#include <linux/ctype.h>
#include "apparmor.h"
#include "inline.h"
size_t sd_getprocattr(struct subdomain *sd, char *str, size_t size)
{
int error = -EACCES; /* default to a perm denied */
size_t len;
if (__sd_is_confined(sd)) {
size_t lena, lenm, lenp = 0;
const char *enforce_str = " (enforce)";
const char *complain_str = " (complain)";
const char *mode_str =
SUBDOMAIN_COMPLAIN(sd) ? complain_str : enforce_str;
lenm = strlen(mode_str);
lena = strlen(sd->active->name);
len = lena;
if (sd->active != sd->profile) {
lenp = strlen(sd->profile->name);
len += (lenp + 1); /* +1 for ^ */
}
/* DONT null terminate strings we output via proc */
len += (lenm + 1); /* for \n */
if (len <= size) {
if (lenp) {
memcpy(str, sd->profile->name, lenp);
str += lenp;
*str++ = '^';
}
memcpy(str, sd->active->name, lena);
str += lena;
memcpy(str, mode_str, lenm);
str += lenm;
*str++ = '\n';
error = len;
} else if (size == 0) {
error = len;
} else {
error = -ERANGE;
}
} else {
const char *unconstrained_str = SD_UNCONSTRAINED "\n";
len = strlen(unconstrained_str);
/* DONT null terminate strings we output via proc */
if (len <= size) {
memcpy(str, unconstrained_str, len);
error = len;
} else if (size == 0) {
error = len;
} else {
error = -ERANGE;
}
}
return error;
}
int sd_setprocattr_changehat(char *hatinfo, size_t infosize)
{
int error = -EINVAL;
char *token = NULL, *hat, *smagic, *tmp;
__u32 magic;
int rc, len, consumed;
unsigned long flags;
SD_DEBUG("%s: %p %zd\n", __FUNCTION__, hatinfo, infosize);
/* strip leading white space */
while (infosize && isspace(*hatinfo)) {
hatinfo++;
infosize--;
}
if (infosize == 0)
goto out;
/*
* Copy string to a new buffer so we can play with it
* It may be zero terminated but we add a trailing 0
* for 100% safety
*/
token = kmalloc(infosize + 1, GFP_KERNEL);
if (!token) {
error = -ENOMEM;
goto out;
}
memcpy(token, hatinfo, infosize);
token[infosize] = 0;
/* error is INVAL until we have at least parsed something */
error = -EINVAL;
tmp = token;
while (*tmp && *tmp != '^') {
tmp++;
}
if (!*tmp || tmp == token) {
SD_WARN("%s: Invalid input '%s'\n", __FUNCTION__, token);
goto out;
}
/* split magic and hat into two strings */
*tmp = 0;
smagic = token;
/*
* Initially set consumed=strlen(magic), as if sscanf
* consumes all input via the %x it will not process the %n
* directive. Otherwise, if sscanf does not consume all the
* input it will process the %n and update consumed.
*/
consumed = len = strlen(smagic);
rc = sscanf(smagic, "%x%n", &magic, &consumed);
if (rc != 1 || consumed != len) {
SD_WARN("%s: Invalid hex magic %s\n",
__FUNCTION__,
smagic);
goto out;
}
hat = tmp + 1;
if (!*hat)
hat = NULL;
if (!hat && !magic) {
SD_WARN("%s: Invalid input, NULL hat and NULL magic\n",
__FUNCTION__);
goto out;
}
SD_DEBUG("%s: Magic 0x%x Hat '%s'\n",
__FUNCTION__, magic, hat ? hat : NULL);
write_lock_irqsave(&sd_lock, flags);
error = sd_change_hat(hat, magic);
write_unlock_irqrestore(&sd_lock, flags);
out:
if (token) {
memset(token, 0, infosize);
kfree(token);
}
return error;
}
int sd_setprocattr_setprofile(struct task_struct *p, char *profilename,
size_t profilesize)
{
int error = -EINVAL;
struct sdprofile *profile;
struct subdomain *sd;
char *name = NULL;
unsigned long flags;
SD_DEBUG("%s: current %s(%d)\n",
__FUNCTION__, current->comm, current->pid);
/* strip leading white space */
while (profilesize && isspace(*profilename)) {
profilename++;
profilesize--;
}
if (profilesize == 0)
goto out;
/*
* Copy string to a new buffer so we guarantee it is zero
* terminated
*/
name = kmalloc(profilesize + 1, GFP_KERNEL);
if (!name) {
error = -ENOMEM;
goto out;
}
strncpy(name, profilename, profilesize);
name[profilesize] = 0;
repeat:
if (strcmp(name, SD_UNCONSTRAINED) == 0)
profile = null_profile;
else
profile = sd_profilelist_find(name);
if (!profile) {
SD_WARN("%s: Unable to switch task %s(%d) to profile '%s'. "
"No such profile.\n",
__FUNCTION__,
p->comm, p->pid,
name);
error = -EINVAL;
goto out;
}
write_lock_irqsave(&sd_lock, flags);
sd = SD_SUBDOMAIN(p->security);
/* switch to unconstrained */
if (profile == null_profile) {
if (__sd_is_confined(sd)) {
SD_WARN("%s: Unconstraining task %s(%d) "
"profile %s active %s\n",
__FUNCTION__,
p->comm, p->pid,
sd->profile->name,
sd->active->name);
sd_switch_unconfined(sd);
} else {
SD_WARN("%s: task %s(%d) "
"is already unconstrained\n",
__FUNCTION__, p->comm, p->pid);
}
} else {
if (!sd) {
/* this task was created before module was
* loaded, allocate a subdomain
*/
SD_WARN("%s: task %s(%d) has no subdomain\n",
__FUNCTION__, p->comm, p->pid);
/* unlock so we can safely GFP_KERNEL */
write_unlock_irqrestore(&sd_lock, flags);
sd = alloc_subdomain(p);
if (!sd) {
SD_WARN("%s: Unable to allocate subdomain for "
"task %s(%d). Cannot confine task to "
"profile %s\n",
__FUNCTION__,
p->comm, p->pid,
name);
error = -ENOMEM;
put_sdprofile(profile);
goto out;
}
write_lock_irqsave(&sd_lock, flags);
if (!p->security) {
p->security = sd;
} else { /* race */
free_subdomain(sd);
sd = SD_SUBDOMAIN(p->security);
}
}
/* ensure the profile hasn't been replaced */
if (unlikely(profile->isstale)) {
WARN_ON(profile == null_complain_profile);
/* drop refcnt obtained from earlier get_sdprofile */
put_sdprofile(profile);
profile = sd_profilelist_find(name);
if (!profile) {
/* Race, profile was removed. */
write_unlock_irqrestore(&sd_lock, flags);
goto repeat;
}
}
/* we do not do a normal task replace since we are not
* replacing with the same profile.
* If existing process is in a hat, it will be moved
* into the new parent profile, even if this new
* profile has a identical named hat.
*/
SD_WARN("%s: Switching task %s(%d) "
"profile %s active %s to new profile %s\n",
__FUNCTION__,
p->comm, p->pid,
sd->profile ? sd->profile->name : SD_UNCONSTRAINED,
sd->active ? sd->profile->name : SD_UNCONSTRAINED,
name);
sd_switch(sd, profile, profile);
put_sdprofile(profile); /* drop ref we obtained above
* from sd_profilelist_find
*/
/* Reset magic in case we were in a subhat before
* This is the only case where we zero the magic after
* calling sd_switch
*/
sd->sd_hat_magic = 0;
}
write_unlock_irqrestore(&sd_lock, flags);
error = 0;
out:
kfree(name);
return error;
}