Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Linux: Kernel

[AppArmor #3 0/12] AppArmor security module

 

 

Linux kernel RSS feed   Index | Next | Previous | View Threaded


john.johansen at canonical

Nov 10, 2009, 8:12 AM

Post #1 of 9 (307 views)
Permalink
[AppArmor #3 0/12] AppArmor security module

This is the newest version of the AppArmor security module it has been
rewritten to use the security_path hooks instead of the previous vfs
approach. The current implementation is aimed at being as semantically
close to previous versions of AppArmor as possible while using the
existing LSM infrastructure.

The rewrite is functional and roughly equivalent to previous versions
of AppArmor based off of vfs patching. Development is on going and
improvements to file, capability, network, resource usage and ipc mediation
are planned.

_Issues NOT currently addressed and will be address in the next post_
* AppArmor audit framework has not yet been updated as suggested by
Eric Paris in
http://marc.info/?l=linux-security-module&m=125778105017307&w=2
* AppArmor mmap_min_addr is broken and needs to be fixed as pointed out by
Eric Paris in
http://marc.info/?l=linux-security-module&m=125778004815241&w=2

_Issues Addressed Since Last Time AppArmor was Posted_
* Implemented change recommended by Tetsuo Handa in feedback:
http://marc.info/?l=linux-security-module&m=125730973023168&w=2
http://marc.info/?l=linux-security-module&m=125740018700307&w=2
- removed read head reset in policy_unpack
- added addition comments on locking, refcounting, and memory allocation
- reworked ref counting some so that more references are held explicitly
- drop dead/unreachable code
- fix oops in putting caps cache cpu_local var
- fix refcounting bug causing leak of creds
- reworked __d_path race detection and removal of (deleted) string
* fixed bug in nameresolution failure in apparmor_bprm_set_creds that could
cause a null pointer dereference oops
* fix bug in removal of child profiles that would lead to null pointer
dereference oops. Cleaned up code and removed dead portions
* rework filter and newest profile cleaning them up after changes made for
above removal bug.
* Cleanup namespace code, removing unused fns and adding addition comments
* move profile load/replace/remove routines from policy_unpack.c to policy.c
this allowed cleaning up the interface, allowing for more core policy
functions to be static, and also allows policy_unpack to only contain
unpack code.


AppArmor documentation can currently be found at
http://developer.novell.com/wiki/index.php/Apparmor

The unflattened AppArmor git tree can be found at
git://kernel.ubuntu.com/jj/apparmor-mainline.git


The AppArmor project is currently in transition and will be moving
away from Novell forge. The current upstream for the AppArmor tools
can be found at
https://launchpad.net/apparmor

The final location of the documentation and mailing lists have
not been determined and will be updated when known.


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


shemminger at vyatta

Nov 13, 2009, 9:44 AM

Post #2 of 9 (288 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

On Tue, 10 Nov 2009 08:12:53 -0800
John Johansen <john.johansen [at] canonical> wrote:

> This is the newest version of the AppArmor security module it has been
> rewritten to use the security_path hooks instead of the previous vfs
> approach. The current implementation is aimed at being as semantically
> close to previous versions of AppArmor as possible while using the
> existing LSM infrastructure.

Does it fix the problem reported as the #1 failure on kernel oops:

Oops 718946 first showed up in kernel version 2.6.31-14-generic
Oops 718946 last showed up in version 2.6.31-13-generic
2.6.31 -- 512

BUG: unable to handle kernel NULL pointer dereference at 00000040
IP: [] apparmor_bprm_set_creds+0x370/0x400
*pde = 00000000
Oops: 0000 [#1] SMP
last sysfs file: /sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT1/charge_full
Modules linked in: binfmt_misc ppdev lp parport joydev snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_pcm_oss mmc_block snd_mixer_oss snd_pcm snd_seq_dummy arc4 ecb snd_seq_oss snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq ath5k acerhdf mac80211 snd_timer ath uvcvideo videodev sdhci_pci snd_seq_device psmouse sdhci v4l1_compat serio_raw cfg80211 jmb38x_ms memstick led_class snd soundcore snd_page_alloc usbhid r8169 mii fbcon tileblit font bitblit softcursor i915 drm i2c_algo_bit video output intel_agp agpgart

Pid: 3316, comm: hamachi-init Not tainted (2.6.31-10-generic #32-Ubuntu) AOA110
EIP: 0060:[] EFLAGS: 00010246 CPU: 0
EIP is at apparmor_bprm_set_creds+0x370/0x400
EAX: fffffffe EBX: dde0fe00 ECX: de59df00 EDX: dd4bfee2
ESI: 00000000 EDI: ddf73ba0 EBP: de59df44 ESP: de59deb4
DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
Process hamachi-init (pid: 3316, ti=de59c000 task=de02d7f0 task.ti=de59c000)
Stack:
de59df00 00000000 00000000 de59ded0 c01c9e40 de5147e8 dec04080 de59deec
<0> c01c9e8f 00000000 00000000 de5147e8 00000000 de5147e8 00000000 000000d0
<0> fffffffe c06ff3a2 00000000 dd4bfee2 00000000 00000000 00000000 00000000
Call Trace:
[] ? __vma_link_rb+0x30/0x40
[] ? __vma_link+0x3f/0x80
[] ? security_bprm_set_creds+0xc/0x10
[] ? prepare_binprm+0xa1/0xf0
[] ? T.626+0x3b/0x50
[] ? do_execve+0x17e/0x2c0
[] ? strncpy_from_user+0x35/0x60
[] ? sys_execve+0x28/0x60
[] ? syscall_call+0x7/0xb
Code: 24 8b 44 24 18 e8 71 f4 ff ff 3d 00 f0 ff ff 89 c1 76 a7 0f b7 44 24 60 f6 c4 40 74 50 c7 44 24 48 a7 f3 6f c0 e9 98 fe ff ff 90 46 40 08 0f 84 e6 fe ff ff e9 d9 fe ff ff 90 8b 54 24 4c 8b
EIP: [] apparmor_bprm_set_creds+0x370/0x400 SS:ESP 0068:de59deb4
CR2: 0000000000000040
---[ end trace 203b1750ff60d177 ]---

http://kerneloops.org/guilty.php?guilty=apparmor_bprm_set_creds&version=2.6.31-release&start=2064384&end=2097151&class=oops
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


john.johansen at canonical

Nov 13, 2009, 9:58 AM

Post #3 of 9 (288 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Stephen Hemminger wrote:
> On Tue, 10 Nov 2009 08:12:53 -0800
> John Johansen <john.johansen [at] canonical> wrote:
>
>> This is the newest version of the AppArmor security module it has been
>> rewritten to use the security_path hooks instead of the previous vfs
>> approach. The current implementation is aimed at being as semantically
>> close to previous versions of AppArmor as possible while using the
>> existing LSM infrastructure.
>
> Does it fix the problem reported as the #1 failure on kernel oops:
>
> Oops 718946 first showed up in kernel version 2.6.31-14-generic
> Oops 718946 last showed up in version 2.6.31-13-generic
> 2.6.31 -- 512

yes, and several others oops as well. I have also identified a couple other
oops that will have fixes in push #4.

thanks
john
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


penguin-kernel at I-love

Nov 20, 2009, 9:39 AM

Post #4 of 9 (277 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Hello.

Sorry for late response.
I browsed apparmorfs-24.c apparmorfs.c audit.c capability.c context.c domain.c .
Comments are shown below.



> static int aa_audit_base(int type, struct aa_profile *profile,
> struct aa_audit *sa, struct audit_context *audit_cxt,
> void (*cb) (struct audit_buffer *, void *))
(...snipped...)
> if (profile && PROFILE_KILL(profile) && type == AUDIT_APPARMOR_DENIED)
> type = AUDIT_APPARMOR_KILL;

PROFILE_KILL(profile) includes profile != NULL checks.
Are you doing

profile && PROFILE_KILL(profile)

in order to ignore aa_g_profile_mode == APPARMOR_KILL if profile == NULL?



> int aa_restore_previous_profile(u64 token)
> {
> struct aa_task_context *cxt;
> struct cred *new = prepare_creds();
> if (!new)
> return -ENOMEM;
>
> cxt = new->security;
> if (cxt->sys.token != token) {
> abort_creds(new);

I think simply returning -EACCES when trying to escape from some profile
gives hijacked process chances to do brute force attack.
Don't you need to kill the current process?

> return -EACCES;
> }



> static struct aa_profile *x_to_profile(struct aa_namespace *ns,
> struct aa_profile *profile,
> const char *name, u16 xindex)
(...snipped...)
> case AA_X_TABLE:
> if (index > profile->file.trans.size) {

profile->file.trans.table[0] is not permitted if profile->file.trans.size == 0,
is it?
Did you mean index >= profile->file.trans.size ?

> AA_ERROR("Invalid named transition\n");
> return ERR_PTR(-EACCES);
> }
> name = profile->file.trans.table[index];
(...snipped...)
> } else if (*name == ':') {
> /* switching namespace */
> const char *ns_name = name + 1;
> name = xname = ns_name + strlen(ns_name) + 1;
> if (!*xname)

Isn't *xname undefined because it is beyond '\0'?

> /* no name so use profile name */
> xname = profile->fqname;

(...snipped...)

> } else if (*name == '@') {
> /* TODO: variable support */
>

You want "continue;" here in order to avoid doing strstr(NULL, "//")
inside aa_find_profile().

> } else {



> int apparmor_bprm_set_creds(struct linux_binprm *bprm)
> {
> struct aa_task_context *cxt;
> struct aa_profile *profile, *new_profile = NULL;
> struct aa_namespace *ns;
> char *buffer = NULL;
> unsigned int state = DFA_START;
> struct path_cond cond = {
> bprm->file->f_path.dentry->d_inode->i_uid,
> bprm->file->f_path.dentry->d_inode->i_mode
> };
> struct aa_audit_file sa = {
> .base.operation = "exec",
> .base.gfp_mask = GFP_KERNEL,
> .request = MAY_EXEC,
> .cond = &cond,
> };
>
> sa.base.error = cap_bprm_set_creds(bprm);
> if (sa.base.error)
> return sa.base.error;
>
> if (bprm->cred_prepared)
> return 0;
>
> cxt = bprm->cred->security;
> BUG_ON(!cxt);
>
> profile = aa_confining_profile(cxt->sys.profile);
> ns = profile->ns;

aa_confining_profile() may return NULL.
According to apparmor-kermic tree, it is

ns = cxt->sys.profile->ns;

>
> /* buffer freed below, name is pointer inside of buffer */
> sa.base.error = aa_get_name(&bprm->file->f_path, 0, &buffer,
> (char **)&sa.name);
> if (sa.base.error) {
> if (!profile || profile->flags & PFLAG_IX_ON_NAME_ERROR)
> sa.base.error = 0;
> sa.base.info = "Exec failed name resolution";
> sa.name = bprm->filename;
> goto audit;
> }
>
> if (!profile) {
> /* unconfined task - attach profile if one matches */
> new_profile = aa_sys_find_attach(&ns->base, sa.name);
> if (!new_profile)
> goto cleanup;
> goto apply;
> } else if (cxt->sys.onexec) {
> /*
> * onexec permissions are stored in a pair, rewalk the
> * dfa to get start of the exec path match.
> */
> sa.perms = change_profile_perms(profile, cxt->sys.onexec->ns,
> sa.name, &state);
> state = aa_dfa_null_transition(profile->file.dfa, state);
> }
> sa.perms = aa_str_perms(profile->file.dfa, state, sa.name, &cond, NULL);>
> if (cxt->sys.onexec && sa.perms.allowed & AA_MAY_ONEXEC) {
> new_profile = cxt->sys.onexec;
> cxt->sys.onexec = NULL;
> sa.base.info = "change_profile onexec";
> } else if (sa.perms.allowed & MAY_EXEC) {
> new_profile = x_to_profile(ns, profile, sa.name,
> sa.perms.xindex);
> if (IS_ERR(new_profile)) {
> if (sa.perms.xindex & AA_X_INHERIT) {
> /* (p|c|n)ix - don't change profile */
> sa.base.info = "ix fallback";
> goto x_clear;
> } else if (sa.perms.xindex & AA_X_UNCONFINED) {
> new_profile = aa_get_profile(ns->unconfined);
> sa.base.info = "ux fallback";

IS_ERR(new_profile) is true but new_profile == NULL is false.
What I worry is that you sometimes embed error values into pointer but you
are sometimes checking only NULL.

> } else {
> sa.base.error = PTR_ERR(new_profile);
> if (sa.base.error == -ENOENT)
> sa.base.info = "profile not found";
> new_profile = NULL;
> }
> }
> } else if (PROFILE_COMPLAIN(profile)) {
> new_profile = aa_alloc_null_profile(profile, 0);
> sa.base.error = -EACCES;
> if (!new_profile)
> sa.base.error = -ENOMEM;
> sa.name2 = new_profile->fqname;

This will oops if new_profile == NULL. Please fix apparmor-karmic as well.

> sa.perms.xindex |= AA_X_UNSAFE;
> } else {
> sa.base.error = -EACCES;
> }
>
> if (!new_profile)
> goto audit;

You want to do

if (!new_profile || IS_ERR(new_profile))

rather than

if (!new_profile)

Please fix apparmor-karmic as well.

>
> if (profile == new_profile) {
> aa_put_profile(new_profile);

aa_put_profile() with error pointer, which will be fixed by above change.

> goto audit;
> }
>
> if (bprm->unsafe & LSM_UNSAFE_SHARE) {
> /* FIXME: currently don't mediate shared state */
> ;
> }
>
> if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
> sa.base.error = aa_may_change_ptraced_domain(current,
> new_profile);

aa_may_change_ptraced_domain() with error pointer, which will be fixed by
above change.

> if (sa.base.error)
> goto audit;
> }
>
> /* Determine if secure exec is needed.
> * Can be at this point for the following reasons:
> * 1. unconfined switching to confined
> * 2. confined switching to different confinement
> * 3. confined switching to unconfined
> *
> * Cases 2 and 3 are marked as requiring secure exec
> * (unless policy specified "unsafe exec")
> *
> * bprm->unsafe is used to cache the AA_X_UNSAFE permission
> * to avoid having to recompute in secureexec
> */
> if (!(sa.perms.xindex & AA_X_UNSAFE))
> bprm->unsafe |= AA_SECURE_X_NEEDED;
>
> apply:
> sa.name2 = new_profile->fqname;

error pointer, which will be fixed by above change.

> /* When switching namespace ensure its part of audit message */
> if (new_profile->ns != ns)
> sa.name3 = new_profile->ns->base.name;
>
> /* when transitioning profiles clear unsafe personality bits */
> bprm->per_clear |= PER_CLEAR_ON_SETID;
>
> aa_put_profile(cxt->sys.profile);
> /* transfer new profile reference will be released when cxt is freed */
> cxt->sys.profile = new_profile;
>
> x_clear:
> aa_put_profile(cxt->sys.previous);
> aa_put_profile(cxt->sys.onexec);
> cxt->sys.previous = NULL;
> cxt->sys.onexec = NULL;
> cxt->sys.token = 0;
>
> audit:
> sa.base.error = aa_audit_file(profile, &sa);

aa_audit_file() might return 0 if PROFILE_COMPLAIN(profile) == true even if
sa.base.error != 0 . I think regarding execve(), we should not ignore errors
like -EACCES, -ENOMEM etc. if something went wrong before auditing.
Otherwise, current process might continue execve() with unexpected profile.

>
> cleanup:
> kfree(buffer);
>
> return sa.base.error;
> }



> int aa_change_hat(const char *hat_name, u64 token, int permtest)
(...snipped...)
> /* freed below */
> name = new_compound_name(root->fqname, hat_name);
>

Audit log lacks "name=%s" part if name == NULL.

> sa.name = name;
> sa.base.info = "hat not found";
> sa.base.error = -ENOENT;



> int aa_change_profile(const char *ns_name, const char *fqname, int onexec,
> int permtest)
(...snipped...)
> /* released below */
> target = aa_find_profile(ns, fqname);
> if (!target) {
> sa.base.info = "profile not found";
> sa.base.error = -ENOENT;
> if (permtest || !PROFILE_COMPLAIN(profile))
> goto audit;
> /* release below */
> target = aa_alloc_null_profile(profile, 0);

aa_alloc_null_profile() will oops if profile == NULL.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


penguin-kernel at I-love

Nov 20, 2009, 9:28 PM

Post #5 of 9 (274 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Regarding file.c ipc.c lib.c lsm.c



You can use container_of() inside callback functions to avoid "void *".

> int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa,
> void (*cb) (struct audit_buffer *, void *))

int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa,
void (*cb) (struct audit_buffer *, struct aa_audit *))

> static int aa_audit_base(int type, struct aa_profile *profile,
> struct aa_audit *sa, struct audit_context *audit_cxt,
> void (*cb) (struct audit_buffer *, void *))

static int aa_audit_base(int type, struct aa_profile *profile,
struct aa_audit *sa, struct audit_context *audit_cxt,
void (*cb) (struct audit_buffer *, struct aa_audit *))

> void file_audit_cb(struct audit_buffer *ab, void *va)
> {
> struct aa_audit_file *sa = va;

void file_audit_cb(struct audit_buffer *ab, struct aa_audit *va)
{
struct aa_audit_file *sa = container_of(va, struct aa_audit_file, base);

> int aa_audit_file(struct aa_profile *profile, struct aa_audit_file *sa)
> (...snipped...)
> return aa_audit(type, profile, (struct aa_audit *)sa, file_audit_cb);

return aa_audit(type, profile, &sa->base, file_audit_cb);

> }

> static void audit_cb(struct audit_buffer *ab, void *va)
> {
> struct aa_audit_ptrace *sa = va;

static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
{
struct aa_audit_ptrace *sa = container_of(va, struct aa_audit_ptrace,
base);

> static int aa_audit_ptrace(struct aa_profile *profile,
> struct aa_audit_ptrace *sa)
> {
> return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa,
> audit_cb);

return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa->base, audit_cb);



> int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
> struct path *new_dir, struct dentry *new_dentry)
(.,..snipped...)
> unsigned int state;
(.,..snipped...)
> sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, &cond,
> &state);

"state" remains uninitialized if profile->file.dfa == NULL.
Are you sure profile->file.dfa != NULL ?



> char *aa_strchrnul(const char *s, int c)
> {
> for (; *s != (char)c && *s != '\0'; ++s)
> ;
> return (char *)s;
> }

Only fqname_subname() calls aa_strchrnul() and
fqname_subname() returns NULL if aa_strchrnul() returns '\0'.
You can use strchr() instead.

> static const char *fqname_subname(const char *name)
> {
> char *split;
> /* check for namespace which begins with a : and ends with : or \0 */
> name = strstrip((char *)name);
> if (*name == ':') {
> split = aa_strchrnul(name + 1, ':');
> if (*split == '\0')
> return NULL;

split = strchr(name + 1, ':');
if (!split)
return NULL;

> name = strstrip(split + 1);
> }
> for (split = strstr(name, "//"); split; split = strstr(name, "//"))
> name = split + 2;
>
> return name;
> }



> char *aa_split_name_from_ns(char *args, char **ns_name)
> {
> char *name = strstrip(args);
>
> *ns_name = NULL;
> if (args[0] == ':') {
> char *split = strstrip(strchr(&args[1], ':'));
>
> if (!split)
> return NULL;


strchr() returns NULL if not found, and strstrip(NULL) will do strlen(NULL).
strstrip() never returns NULL. Did you mean

char *split = strchr(&args[1], ':');

if (!split)
return NULL;
split = strstrip(split);

?

>
> *split = 0;
> *ns_name = &args[1];
> name = strstrip(split + 1);
> }
> if (*name == 0)
> name = NULL;
>
> return name;
> }



> static int apparmor_sysctl(struct ctl_table *table, int op)
This hook will be removed.

> char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen)
This function will no longer be needed.

> int aa_pathstr_perm(struct aa_profile *profile, const char *op,
> const char *name, u16 request, struct path_cond *cond)
This function will no longer be needed.



> static int apparmor_file_permission(struct file *file, int mask)
> {
> /*
> * TODO: cache profiles that have revalidated?
> */
> struct aa_file_cxt *fcxt = file->f_security;
> struct aa_profile *profile, *fprofile = fcxt->profile;
> int error = 0;
>
> if (!fprofile || !file->f_path.mnt ||
> !mediated_filesystem(file->f_path.dentry->d_inode))
> return 0;
>
> profile = aa_current_profile();
>
> #ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
> /*
> * AppArmor <= 2.4 revalidates files at access time instead
> * of at exec.
> */
> if (profile && ((fprofile != profile) || (mask & ~fcxt->allowed)))
> error = aa_file_perm(profile, "file_perm", file, mask);
> #endif
>
> return error;
> }

Why need to call this function if CONFIG_SECURITY_APPARMOR_COMPAT_24=n ?
I think we can do

> static struct security_operations apparmor_ops = {
(...snipped...)

#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24

> .file_permission = apparmor_file_permission,

#endif

(...snipped...)
> }



> int aa_alloc_default_namespace(void)

This function could be declared with __init attribute.



> static int __init apparmor_init(void)
(...snipped...)
> error = set_init_cxt();
> if (error) {
> AA_ERROR("Failed to set context on init task\n");
> goto alloc_out;

This should be

goto register_security_out;

in order to call aa_free_default_namespace().

> }
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


penguin-kernel at I-love

Nov 22, 2009, 3:49 AM

Post #6 of 9 (259 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

And the rest of files...



Regarding match.c

Why not to start YYTD_ID_something from 0 so that we can avoid "- 1" in
dfa->tables[table->td_id - 1] ? I think you can do "- 1" at

th.td_id = be16_to_cpu(*(u16 *) (blob));

.



> int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
> {
(...snipped...)
> fail:
> for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
> free_table(dfa->tables[i]);
> dfa->tables[i] = NULL;
> }

This function is called by only aa_unpack_dfa(), and aa_unpack_dfa() calls
aa_match_free(). Thus, you don't need to call free_table() here.

> return error;
> }



Regarding net.c

> static void audit_cb(struct audit_buffer *ab, void *va)
> {
> struct aa_audit_net *sa = va;

static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
{
struct aa_audit_net *sa = container_of(va, struct aa_audit_net, base);

> static int aa_audit_net(struct aa_profile *profile, struct aa_audit_net *sa)
> {
(...snipped...)
> return aa_audit(type, profile, (struct aa_audit *)sa, audit_cb);

return aa_audit(type, profile, &sa->base, audit_cb);

> }



Regarding policy.c

> struct aa_namespace *alloc_aa_namespace(const char *name)

This function could be "static". Please try "make namespacecheck".



> struct aa_namespace *aa_prepare_namespace(const char *name)

This function could be "static".

> {
> struct aa_namespace *ns;
>
> write_lock(&ns_list_lock);
> if (name)
> /* released by caller */
> ns = aa_get_namespace(__aa_find_namespace(&ns_list, name));
> else
> /* released by caller */
> ns = aa_get_namespace(default_namespace);

alloc_aa_namespace() returns NULL if name == NULL.
If it is intended behavior, you may do like

else {
/* released by caller */
ns = aa_get_namespace(default_namespace);
write_unlock(&ns_list_lock);
return ns;
}

> if (!ns) {
> struct aa_namespace *new_ns;
> write_unlock(&ns_list_lock);
> new_ns = alloc_aa_namespace(name);
> if (!new_ns)
> return NULL;
> write_lock(&ns_list_lock);
> /* test for race when new_ns was allocated */
> ns = __aa_find_namespace(&ns_list, name);
> if (!ns) {
> list_add(&new_ns->base.list, &ns_list);
> /* add list ref */
> ns = aa_get_namespace(new_ns);
> } else {
> /* raced so free the new one */
> free_aa_namespace(new_ns);
> /* get reference on namespace */
> aa_get_namespace(ns);
> }
> }
> write_unlock(&ns_list_lock);
>
> /* return ref */
> return ns;
> }



> void __aa_replace_profile(struct aa_profile *old,
> struct aa_profile *new)

This function could be "static".



> void __aa_profile_list_release(struct list_head *head)

This function could be "static".



> void __aa_remove_namespace(struct aa_namespace *ns)

This function could be "static".

> {
> struct aa_profile *unconfined = ns->unconfined;
> /* remove ns from namespace list */
> list_del_init(&ns->base.list);
>
> /*
> * break the ns, unconfined profile cyclic reference and forward
> * all new unconfined profiles requests to the default namespace
> * This will result in all confined tasks that have a profile
> * being removed inheriting the default->unconfined profile.
> */
> ns->unconfined = aa_get_profile(default_namespace->unconfined);
> __aa_profile_list_release(&ns->base.profiles);
> /* release original ns->unconfined ref */
> aa_put_profile(unconfined);
> /* release ns->base.list ref, from removal above */
> aa_put_namespace(ns);

aa_put_profile() and aa_put_namespace() may call write_lock() inside
free_aa_profile(). Are you sure that these calls do not dead lock?

> }



> ssize_t aa_interface_remove_profiles(char *name, size_t size)
(...snipped...)
> write_lock(&ns_list_lock);
> if (name[0] == ':') {
> char *ns_name;
> name = aa_split_name_from_ns(name, &ns_name);
> /* released below */
> ns = aa_get_namespace(__aa_find_namespace(&ns_list, ns_name));

aa_split_name_from_ns() may set ns_name to NULL but __aa_find_namespace() can't
handle ns_name == NULL case. I think you should check ns_name != NULL.



Regarding policy_unpack.c

Please use bool for functions that return 0 or 1.



> static void audit_cb(struct audit_buffer *ab, void *va)
> {
> struct aa_audit_iface *sa = va;

static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
{
struct aa_audit_iface *sa = container_of(va, struct aa_audit_iface,
base);



> static int aa_unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
(...snipped...)
> for (i = 0; i < size; i++) {
> char *tmp;
> if (!unpack_dynstring(e, &tmp, NULL))
> goto fail;
> /*
> * note: strings beginning with a : have an embedded
> * \0 seperating the profile ns name from the profile
> * name
> */
> profile->file.trans.table[i] = tmp;

unpack_dynstring() returns string duplicated by kstrdup(). Thus, "tmp" can't
have an embedded \0 seperating the profile ns name from the profile name
even if tmp[0] == ':' is true, can it?

> }



Regarding resource.c

> static void audit_cb(struct audit_buffer *ab, void *va)
> {
> struct aa_audit_resource *sa = va;

static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
{
struct aa_audit_resource *sa = container_of(va, struct aa_audit_resource,
base);



> static int aa_audit_resource(struct aa_profile *profile,
> struct aa_audit_resource *sa)
> {
> return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa,
> audit_cb);

return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa->base, audit_cb);

> }



Regarding sid.c

> int aa_add_sid_profile(u32 sid, struct aa_profile *profile)

This function is not used.



> int aa_replace_sid_profile(u32 sid, struct aa_profile *profile)

This function is not used.



> struct aa_profile *aa_get_sid_profile(u32 sid)

This function is not used.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


john.johansen at canonical

Nov 23, 2009, 2:10 AM

Post #7 of 9 (254 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Tetsuo Handa wrote:
> Hello.
>
> Sorry for late response.
np. we are all busy and any time taken to review code is greatly appreciated

> I browsed apparmorfs-24.c apparmorfs.c audit.c capability.c context.c domain.c .
> Comments are shown below.
>
>
>
>> static int aa_audit_base(int type, struct aa_profile *profile,
>> struct aa_audit *sa, struct audit_context *audit_cxt,
>> void (*cb) (struct audit_buffer *, void *))
> (...snipped...)
>> if (profile && PROFILE_KILL(profile) && type == AUDIT_APPARMOR_DENIED)
>> type = AUDIT_APPARMOR_KILL;
>
> PROFILE_KILL(profile) includes profile != NULL checks.
> Are you doing
>
> profile && PROFILE_KILL(profile)
>
> in order to ignore aa_g_profile_mode == APPARMOR_KILL if profile == NULL?
>
yes. The profile kill flag should not be set unless there is a profile
confining the task. This is confusing and really should be reworked, by
either renaming PROFILE_KILL and adding a comment here, or reworking the
PROFILE_KILL check, and its callers. In any case I need to go through
and document which functions require filtered vs. unfiltered profiles
(ie. profile != NULL) I'll update this for the next posting.

>
>
>> int aa_restore_previous_profile(u64 token)
>> {
>> struct aa_task_context *cxt;
>> struct cred *new = prepare_creds();
>> if (!new)
>> return -ENOMEM;
>>
>> cxt = new->security;
>> if (cxt->sys.token != token) {
>> abort_creds(new);
>
> I think simply returning -EACCES when trying to escape from some profile
> gives hijacked process chances to do brute force attack.
> Don't you need to kill the current process?
>
>> return -EACCES;
>> }
>
>
correct and we do. It is done by the caller (aa_change_hat()) by setting the
kill flag in the audit structure.
} else if (previous_profile) {
sa.name = previous_profile->fqname;
sa.base.error = aa_restore_previous_profile(token);
sa.perms.kill = AA_MAY_CHANGEHAT;

>
>> static struct aa_profile *x_to_profile(struct aa_namespace *ns,
>> struct aa_profile *profile,
>> const char *name, u16 xindex)
> (...snipped...)
>> case AA_X_TABLE:
>> if (index > profile->file.trans.size) {
>
> profile->file.trans.table[0] is not permitted if profile->file.trans.size == 0,
> is it?
No it isn't.
> Did you mean index >= profile->file.trans.size ?
>
No, a file.trans.size == 0 means there are no transition entries, in which case the
type should not be AA_X_TABLE. It is a consistency check that should never occur
if the user space tools properly generate the policy. This check could, and probably
should be moved to a late pass in the policy unpack, so the check is only ever done
once for a given profile.


>> AA_ERROR("Invalid named transition\n");
>> return ERR_PTR(-EACCES);
>> }
>> name = profile->file.trans.table[index];
> (...snipped...)
>> } else if (*name == ':') {
>> /* switching namespace */
>> const char *ns_name = name + 1;
>> name = xname = ns_name + strlen(ns_name) + 1;
>> if (!*xname)
>
> Isn't *xname undefined because it is beyond '\0'?
>
No, however this needs to be better documented, and perhaps pulled out
into its own function. The encoding of the names:
if there is a namespace the general case is
:<namespace>\0<name>\0
if there is a namespace and the name is not specified, it becomes
:<namespace>\0\0
else if the name does not begin with a : it is just a name
<name>\0


>> /* no name so use profile name */
>> xname = profile->fqname;
>
> (...snipped...)
>
>> } else if (*name == '@') {
>> /* TODO: variable support */
>>
>
> You want "continue;" here in order to avoid doing strstr(NULL, "//")
> inside aa_find_profile().
>
yep, thanks for catching that

>> } else {
>
>
>
>> int apparmor_bprm_set_creds(struct linux_binprm *bprm)
>> {
>> struct aa_task_context *cxt;
>> struct aa_profile *profile, *new_profile = NULL;
>> struct aa_namespace *ns;
>> char *buffer = NULL;
>> unsigned int state = DFA_START;
>> struct path_cond cond = {
>> bprm->file->f_path.dentry->d_inode->i_uid,
>> bprm->file->f_path.dentry->d_inode->i_mode
>> };
>> struct aa_audit_file sa = {
>> .base.operation = "exec",
>> .base.gfp_mask = GFP_KERNEL,
>> .request = MAY_EXEC,
>> .cond = &cond,
>> };
>>
>> sa.base.error = cap_bprm_set_creds(bprm);
>> if (sa.base.error)
>> return sa.base.error;
>>
>> if (bprm->cred_prepared)
>> return 0;
>>
>> cxt = bprm->cred->security;
>> BUG_ON(!cxt);
>>
>> profile = aa_confining_profile(cxt->sys.profile);
>> ns = profile->ns;
>
> aa_confining_profile() may return NULL.
> According to apparmor-kermic tree, it is
>
> ns = cxt->sys.profile->ns;
>
yep, I introduced this error when reworking the profile filtering, while
cleaning up the removed profile checks. I really should have done it
as a separate patch, and I only caught it after I pushed this patch set.
It is already fixed for the next push.

>> /* buffer freed below, name is pointer inside of buffer */
>> sa.base.error = aa_get_name(&bprm->file->f_path, 0, &buffer,
>> (char **)&sa.name);
>> if (sa.base.error) {
>> if (!profile || profile->flags & PFLAG_IX_ON_NAME_ERROR)
>> sa.base.error = 0;
>> sa.base.info = "Exec failed name resolution";
>> sa.name = bprm->filename;
>> goto audit;
>> }
>>
>> if (!profile) {
>> /* unconfined task - attach profile if one matches */
>> new_profile = aa_sys_find_attach(&ns->base, sa.name);
>> if (!new_profile)
>> goto cleanup;
>> goto apply;
>> } else if (cxt->sys.onexec) {
>> /*
>> * onexec permissions are stored in a pair, rewalk the
>> * dfa to get start of the exec path match.
>> */
>> sa.perms = change_profile_perms(profile, cxt->sys.onexec->ns,
>> sa.name, &state);
>> state = aa_dfa_null_transition(profile->file.dfa, state);
>> }
>> sa.perms = aa_str_perms(profile->file.dfa, state, sa.name, &cond, NULL);>
>> if (cxt->sys.onexec && sa.perms.allowed & AA_MAY_ONEXEC) {
>> new_profile = cxt->sys.onexec;
>> cxt->sys.onexec = NULL;
>> sa.base.info = "change_profile onexec";
>> } else if (sa.perms.allowed & MAY_EXEC) {
>> new_profile = x_to_profile(ns, profile, sa.name,
>> sa.perms.xindex);
>> if (IS_ERR(new_profile)) {
>> if (sa.perms.xindex & AA_X_INHERIT) {
>> /* (p|c|n)ix - don't change profile */
>> sa.base.info = "ix fallback";
>> goto x_clear;
>> } else if (sa.perms.xindex & AA_X_UNCONFINED) {
>> new_profile = aa_get_profile(ns->unconfined);
>> sa.base.info = "ux fallback";
>
> IS_ERR(new_profile) is true but new_profile == NULL is false.
> What I worry is that you sometimes embed error values into pointer but you
> are sometimes checking only NULL.
>
Right, the use of ERR_PTR is limited but it is easy to mess up, and/or not follow
and maintenance could be problematic. It might be best to rework so none of
the functions return ERR_PTR, avoiding any potential problems here.

>> } else {
>> sa.base.error = PTR_ERR(new_profile);
>> if (sa.base.error == -ENOENT)
>> sa.base.info = "profile not found";
>> new_profile = NULL;
>> }
>> }
>> } else if (PROFILE_COMPLAIN(profile)) {
>> new_profile = aa_alloc_null_profile(profile, 0);
>> sa.base.error = -EACCES;
>> if (!new_profile)
>> sa.base.error = -ENOMEM;
>> sa.name2 = new_profile->fqname;
>
> This will oops if new_profile == NULL. Please fix apparmor-karmic as well.
>
ouch :(, thanks again

>> sa.perms.xindex |= AA_X_UNSAFE;
>> } else {
>> sa.base.error = -EACCES;
>> }
>>
>> if (!new_profile)
>> goto audit;
>
> You want to do
>
> if (!new_profile || IS_ERR(new_profile))
>
> rather than
>
> if (!new_profile)
>
> Please fix apparmor-karmic as well.
>
Well it shouldn't ever get to this code with an ERR_PTR, it is handled by
new_profile = x_to_profile(ns, profile, sa.name,
sa.perms.xindex);
if (IS_ERR(new_profile)) {
above, but it is too easy to miss this, or break in the future so I will
rework to drop the use of ERR_PTR with profile.

>> if (profile == new_profile) {
>> aa_put_profile(new_profile);
>
> aa_put_profile() with error pointer, which will be fixed by above change.
>
dito

>> goto audit;
>> }
>>
>> if (bprm->unsafe & LSM_UNSAFE_SHARE) {
>> /* FIXME: currently don't mediate shared state */
>> ;
>> }
>>
>> if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
>> sa.base.error = aa_may_change_ptraced_domain(current,
>> new_profile);
>
> aa_may_change_ptraced_domain() with error pointer, which will be fixed by
> above change.
>
dito

>> if (sa.base.error)
>> goto audit;
>> }
>>
>> /* Determine if secure exec is needed.
>> * Can be at this point for the following reasons:
>> * 1. unconfined switching to confined
>> * 2. confined switching to different confinement
>> * 3. confined switching to unconfined
>> *
>> * Cases 2 and 3 are marked as requiring secure exec
>> * (unless policy specified "unsafe exec")
>> *
>> * bprm->unsafe is used to cache the AA_X_UNSAFE permission
>> * to avoid having to recompute in secureexec
>> */
>> if (!(sa.perms.xindex & AA_X_UNSAFE))
>> bprm->unsafe |= AA_SECURE_X_NEEDED;
>>
>> apply:
>> sa.name2 = new_profile->fqname;
>
> error pointer, which will be fixed by above change.
>
dito

>> /* When switching namespace ensure its part of audit message */
>> if (new_profile->ns != ns)
>> sa.name3 = new_profile->ns->base.name;
>>
>> /* when transitioning profiles clear unsafe personality bits */
>> bprm->per_clear |= PER_CLEAR_ON_SETID;
>>
>> aa_put_profile(cxt->sys.profile);
>> /* transfer new profile reference will be released when cxt is freed */
>> cxt->sys.profile = new_profile;
>>
>> x_clear:
>> aa_put_profile(cxt->sys.previous);
>> aa_put_profile(cxt->sys.onexec);
>> cxt->sys.previous = NULL;
>> cxt->sys.onexec = NULL;
>> cxt->sys.token = 0;
>>
>> audit:
>> sa.base.error = aa_audit_file(profile, &sa);
>
> aa_audit_file() might return 0 if PROFILE_COMPLAIN(profile) == true even if
> sa.base.error != 0 . I think regarding execve(), we should not ignore errors
> like -EACCES, -ENOMEM etc. if something went wrong before auditing.
> Otherwise, current process might continue execve() with unexpected profile.
>
true enough, it should only be EPERM, EACCES

>> cleanup:
>> kfree(buffer);
>>
>> return sa.base.error;
>> }
>
>
>
>> int aa_change_hat(const char *hat_name, u64 token, int permtest)
> (...snipped...)
>> /* freed below */
>> name = new_compound_name(root->fqname, hat_name);
>>
>
> Audit log lacks "name=%s" part if name == NULL.
>
hrmm, yeah that is less than ideal, will fix

>> sa.name = name;
>> sa.base.info = "hat not found";
>> sa.base.error = -ENOENT;
>
>
>
>> int aa_change_profile(const char *ns_name, const char *fqname, int onexec,
>> int permtest)
> (...snipped...)
>> /* released below */
>> target = aa_find_profile(ns, fqname);
>> if (!target) {
>> sa.base.info = "profile not found";
>> sa.base.error = -ENOENT;
>> if (permtest || !PROFILE_COMPLAIN(profile))
>> goto audit;
>> /* release below */
>> target = aa_alloc_null_profile(profile, 0);
>
> aa_alloc_null_profile() will oops if profile == NULL.

right this is actually caught by the !PROFILE_COMPLAIN(profile) part of
>> if (permtest || !PROFILE_COMPLAIN(profile))
>> goto audit;
immediately above but that is less than obvious and needs to be documented.

thanks again
john
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


john.johansen at canonical

Nov 23, 2009, 2:10 AM

Post #8 of 9 (253 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Tetsuo Handa wrote:
> And the rest of files...
>
>
>
> Regarding match.c
>
> Why not to start YYTD_ID_something from 0 so that we can avoid "- 1" in
> dfa->tables[table->td_id - 1] ? I think you can do "- 1" at
>
Right this is a legacy bit, to make a long story short they exactly match
the Flex table mappings which is unnecessary as we explicitly are not
compatible with Flex in other ways. It will probably wait until after
the next push as I am looking at accept state cleanup for the dfa as well.

> th.td_id = be16_to_cpu(*(u16 *) (blob));
that is a possibility as long as it got a good comment to explain what
is going on.

>
> .
>
>
>
>> int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
>> {
> (...snipped...)
>> fail:
>> for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
>> free_table(dfa->tables[i]);
>> dfa->tables[i] = NULL;
>> }
>
> This function is called by only aa_unpack_dfa(), and aa_unpack_dfa() calls
> aa_match_free(). Thus, you don't need to call free_table() here.
>
Hrmm, yeah it needs to be reworked, I don't particularly like returning a
partial struct to have it cleaned up later. I think it might be better
to rework aa_unpack_dfa, dropping the aa_match_free and moving the verify
call into the unpack routine, and the above for loop can be replaced
by aa_match_free


>> return error;
>> }
>
>
>
> Regarding net.c
>
>> static void audit_cb(struct audit_buffer *ab, void *va)
>> {
>> struct aa_audit_net *sa = va;
>
> static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
> {
> struct aa_audit_net *sa = container_of(va, struct aa_audit_net, base);
>
>> static int aa_audit_net(struct aa_profile *profile, struct aa_audit_net *sa)
>> {
> (...snipped...)
>> return aa_audit(type, profile, (struct aa_audit *)sa, audit_cb);
>
> return aa_audit(type, profile, &sa->base, audit_cb);
>
>> }
>
>
yep, thanks

>
> Regarding policy.c
>
>> struct aa_namespace *alloc_aa_namespace(const char *name)
>
> This function could be "static". Please try "make namespacecheck".
>
will do

>
>
>> struct aa_namespace *aa_prepare_namespace(const char *name)
>
> This function could be "static".
>
>> {
>> struct aa_namespace *ns;
>>
>> write_lock(&ns_list_lock);
>> if (name)
>> /* released by caller */
>> ns = aa_get_namespace(__aa_find_namespace(&ns_list, name));
>> else
>> /* released by caller */
>> ns = aa_get_namespace(default_namespace);
>
> alloc_aa_namespace() returns NULL if name == NULL.
> If it is intended behavior, you may do like
>
> else {
> /* released by caller */
> ns = aa_get_namespace(default_namespace);
> write_unlock(&ns_list_lock);
> return ns;
> }
>
well at this point name != NULL because in that case ns == default_namespace,
but it should be documented that name can not be null here.

In general I am not happy with prepare_namespace and will take another look
at this.

>> if (!ns) {
>> struct aa_namespace *new_ns;
>> write_unlock(&ns_list_lock);
>> new_ns = alloc_aa_namespace(name);
>> if (!new_ns)
>> return NULL;
>> write_lock(&ns_list_lock);
>> /* test for race when new_ns was allocated */
>> ns = __aa_find_namespace(&ns_list, name);
>> if (!ns) {
>> list_add(&new_ns->base.list, &ns_list);
>> /* add list ref */
>> ns = aa_get_namespace(new_ns);
>> } else {
>> /* raced so free the new one */
>> free_aa_namespace(new_ns);
>> /* get reference on namespace */
>> aa_get_namespace(ns);
>> }
>> }
>> write_unlock(&ns_list_lock);
>>
>> /* return ref */
>> return ns;
>> }
>
>
>
>> void __aa_replace_profile(struct aa_profile *old,
>> struct aa_profile *new)
>
> This function could be "static".
>
>
>
>> void __aa_profile_list_release(struct list_head *head)
>
> This function could be "static".
>
>
>
>> void __aa_remove_namespace(struct aa_namespace *ns)
>
> This function could be "static".
>
>> {
>> struct aa_profile *unconfined = ns->unconfined;
>> /* remove ns from namespace list */
>> list_del_init(&ns->base.list);
>>
>> /*
>> * break the ns, unconfined profile cyclic reference and forward
>> * all new unconfined profiles requests to the default namespace
>> * This will result in all confined tasks that have a profile
>> * being removed inheriting the default->unconfined profile.
>> */
>> ns->unconfined = aa_get_profile(default_namespace->unconfined);
>> __aa_profile_list_release(&ns->base.profiles);
>> /* release original ns->unconfined ref */
>> aa_put_profile(unconfined);
>> /* release ns->base.list ref, from removal above */
>> aa_put_namespace(ns);
>
> aa_put_profile() and aa_put_namespace() may call write_lock() inside
> free_aa_profile(). Are you sure that these calls do not dead lock?
>
Yes, though I will give it another run through and reverify and add better
comments. The locking is such that the profile should be removed from
the list before free_aa_profile is called, and once in free_aa_profile
the lock is taken and released before any put_ is done.

>> }
>
>
>
>> ssize_t aa_interface_remove_profiles(char *name, size_t size)
> (...snipped...)
>> write_lock(&ns_list_lock);
>> if (name[0] == ':') {
>> char *ns_name;
>> name = aa_split_name_from_ns(name, &ns_name);
>> /* released below */
>> ns = aa_get_namespace(__aa_find_namespace(&ns_list, ns_name));
>
> aa_split_name_from_ns() may set ns_name to NULL but __aa_find_namespace() can't
> handle ns_name == NULL case. I think you should check ns_name != NULL.
>
yep

>
>
> Regarding policy_unpack.c
>
> Please use bool for functions that return 0 or 1.
>
>
>
>> static void audit_cb(struct audit_buffer *ab, void *va)
>> {
>> struct aa_audit_iface *sa = va;
>
> static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
> {
> struct aa_audit_iface *sa = container_of(va, struct aa_audit_iface,
> base);
>
>
>
>> static int aa_unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
> (...snipped...)
>> for (i = 0; i < size; i++) {
>> char *tmp;
>> if (!unpack_dynstring(e, &tmp, NULL))
>> goto fail;
>> /*
>> * note: strings beginning with a : have an embedded
>> * \0 seperating the profile ns name from the profile
>> * name
>> */
>> profile->file.trans.table[i] = tmp;
>
> unpack_dynstring() returns string duplicated by kstrdup(). Thus, "tmp" can't
> have an embedded \0 seperating the profile ns name from the profile name
> even if tmp[0] == ':' is true, can it?
>
indeed, I should not have switched to kstrdup.

>> }
>
>
>
> Regarding resource.c
>
>> static void audit_cb(struct audit_buffer *ab, void *va)
>> {
>> struct aa_audit_resource *sa = va;
>
> static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
> {
> struct aa_audit_resource *sa = container_of(va, struct aa_audit_resource,
> base);
>
>
>
>> static int aa_audit_resource(struct aa_profile *profile,
>> struct aa_audit_resource *sa)
>> {
>> return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa,
>> audit_cb);
>
> return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa->base, audit_cb);
>
>> }
>
>
>
> Regarding sid.c
>
>> int aa_add_sid_profile(u32 sid, struct aa_profile *profile)
>
> This function is not used.
>
>
>
>> int aa_replace_sid_profile(u32 sid, struct aa_profile *profile)
>
> This function is not used.
>
>
>
>> struct aa_profile *aa_get_sid_profile(u32 sid)
>
> This function is not used.

right will fix

thanks
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/


john.johansen at canonical

Nov 23, 2009, 2:11 AM

Post #9 of 9 (252 views)
Permalink
Re: [AppArmor #3 0/12] AppArmor security module [In reply to]

Tetsuo Handa wrote:
> Regarding file.c ipc.c lib.c lsm.c
>
>
>
> You can use container_of() inside callback functions to avoid "void *".
>
yeah that is cleaner, will do

>> int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa,
>> void (*cb) (struct audit_buffer *, void *))
>
> int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa,
> void (*cb) (struct audit_buffer *, struct aa_audit *))
>
>> static int aa_audit_base(int type, struct aa_profile *profile,
>> struct aa_audit *sa, struct audit_context *audit_cxt,
>> void (*cb) (struct audit_buffer *, void *))
>
> static int aa_audit_base(int type, struct aa_profile *profile,
> struct aa_audit *sa, struct audit_context *audit_cxt,
> void (*cb) (struct audit_buffer *, struct aa_audit *))
>
>> void file_audit_cb(struct audit_buffer *ab, void *va)
>> {
>> struct aa_audit_file *sa = va;
>
> void file_audit_cb(struct audit_buffer *ab, struct aa_audit *va)
> {
> struct aa_audit_file *sa = container_of(va, struct aa_audit_file, base);
>
>> int aa_audit_file(struct aa_profile *profile, struct aa_audit_file *sa)
>> (...snipped...)
>> return aa_audit(type, profile, (struct aa_audit *)sa, file_audit_cb);
>
> return aa_audit(type, profile, &sa->base, file_audit_cb);
>
>> }
>
>> static void audit_cb(struct audit_buffer *ab, void *va)
>> {
>> struct aa_audit_ptrace *sa = va;
>
> static void audit_cb(struct audit_buffer *ab, struct aa_audit *va)
> {
> struct aa_audit_ptrace *sa = container_of(va, struct aa_audit_ptrace,
> base);
>
>> static int aa_audit_ptrace(struct aa_profile *profile,
>> struct aa_audit_ptrace *sa)
>> {
>> return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa,
>> audit_cb);
>
> return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa->base, audit_cb);
>
>
>
>> int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
>> struct path *new_dir, struct dentry *new_dentry)
> (.,..snipped...)
>> unsigned int state;
> (.,..snipped...)
>> sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, &cond,
>> &state);
>
> "state" remains uninitialized if profile->file.dfa == NULL.
> Are you sure profile->file.dfa != NULL ?
>
No this needs to be fixed.

>
>
>> char *aa_strchrnul(const char *s, int c)
>> {
>> for (; *s != (char)c && *s != '\0'; ++s)
>> ;
>> return (char *)s;
>> }
>
> Only fqname_subname() calls aa_strchrnul() and
> fqname_subname() returns NULL if aa_strchrnul() returns '\0'.
> You can use strchr() instead.
>
hrmm right thanks

>> static const char *fqname_subname(const char *name)
>> {
>> char *split;
>> /* check for namespace which begins with a : and ends with : or \0 */
>> name = strstrip((char *)name);
>> if (*name == ':') {
>> split = aa_strchrnul(name + 1, ':');
>> if (*split == '\0')
>> return NULL;
>
> split = strchr(name + 1, ':');
> if (!split)
> return NULL;
>
yep

>> name = strstrip(split + 1);
>> }
>> for (split = strstr(name, "//"); split; split = strstr(name, "//"))
>> name = split + 2;
>>
>> return name;
>> }
>
>
>
>> char *aa_split_name_from_ns(char *args, char **ns_name)
>> {
>> char *name = strstrip(args);
>>
>> *ns_name = NULL;
>> if (args[0] == ':') {
>> char *split = strstrip(strchr(&args[1], ':'));
>>
>> if (!split)
>> return NULL;
>
>
> strchr() returns NULL if not found, and strstrip(NULL) will do strlen(NULL).
> strstrip() never returns NULL. Did you mean
>
> char *split = strchr(&args[1], ':');
>
> if (!split)
> return NULL;
> split = strstrip(split);
>
> ?
yes, thanks

>
>> *split = 0;
>> *ns_name = &args[1];
>> name = strstrip(split + 1);
>> }
>> if (*name == 0)
>> name = NULL;
>>
>> return name;
>> }
>
>
>
>> static int apparmor_sysctl(struct ctl_table *table, int op)
> This hook will be removed.
>
>> char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen)
> This function will no longer be needed.
>
>> int aa_pathstr_perm(struct aa_profile *profile, const char *op,
>> const char *name, u16 request, struct path_cond *cond)
> This function will no longer be needed.
>
yep

>
>
>> static int apparmor_file_permission(struct file *file, int mask)
>> {
>> /*
>> * TODO: cache profiles that have revalidated?
>> */
>> struct aa_file_cxt *fcxt = file->f_security;
>> struct aa_profile *profile, *fprofile = fcxt->profile;
>> int error = 0;
>>
>> if (!fprofile || !file->f_path.mnt ||
>> !mediated_filesystem(file->f_path.dentry->d_inode))
>> return 0;
>>
>> profile = aa_current_profile();
>>
>> #ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
>> /*
>> * AppArmor <= 2.4 revalidates files at access time instead
>> * of at exec.
>> */
>> if (profile && ((fprofile != profile) || (mask & ~fcxt->allowed)))
>> error = aa_file_perm(profile, "file_perm", file, mask);
>> #endif
>>
>> return error;
>> }
>
> Why need to call this function if CONFIG_SECURITY_APPARMOR_COMPAT_24=n ?
> I think we can do
>
>> static struct security_operations apparmor_ops = {
> (...snipped...)
>
> #ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
>
>> .file_permission = apparmor_file_permission,
>
> #endif
>
yes we can currently. Though this will change in the future, but for now
we should got with the cleaner switch.

> (...snipped...)
>> }
>
>
>
>> int aa_alloc_default_namespace(void)
>
> This function could be declared with __init attribute.
>
yep, thanks

>
>
>> static int __init apparmor_init(void)
> (...snipped...)
>> error = set_init_cxt();
>> if (error) {
>> AA_ERROR("Failed to set context on init task\n");
>> goto alloc_out;
>
> This should be
>
> goto register_security_out;
>
> in order to call aa_free_default_namespace().
>
>> }

indeed

thanks again for taking the time to review
john

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo [at] vger
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Linux kernel RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.