123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580 |
- /*
- * AppArmor security module
- *
- * This file contains AppArmor /sys/kernel/security/apparmor interface functions
- *
- * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
- *
- * 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.
- */
- #include <linux/ctype.h>
- #include <linux/security.h>
- #include <linux/vmalloc.h>
- #include <linux/module.h>
- #include <linux/seq_file.h>
- #include <linux/uaccess.h>
- #include <linux/mount.h>
- #include <linux/namei.h>
- #include <linux/capability.h>
- #include <linux/rcupdate.h>
- #include <linux/fs.h>
- #include <linux/poll.h>
- #include <uapi/linux/major.h>
- #include <uapi/linux/magic.h>
- #include "include/apparmor.h"
- #include "include/apparmorfs.h"
- #include "include/audit.h"
- #include "include/cred.h"
- #include "include/crypto.h"
- #include "include/ipc.h"
- #include "include/label.h"
- #include "include/policy.h"
- #include "include/policy_ns.h"
- #include "include/resource.h"
- #include "include/policy_unpack.h"
- /*
- * The apparmor filesystem interface used for policy load and introspection
- * The interface is split into two main components based on their function
- * a securityfs component:
- * used for static files that are always available, and which allows
- * userspace to specificy the location of the security filesystem.
- *
- * fns and data are prefixed with
- * aa_sfs_
- *
- * an apparmorfs component:
- * used loaded policy content and introspection. It is not part of a
- * regular mounted filesystem and is available only through the magic
- * policy symlink in the root of the securityfs apparmor/ directory.
- * Tasks queries will be magically redirected to the correct portion
- * of the policy tree based on their confinement.
- *
- * fns and data are prefixed with
- * aafs_
- *
- * The aa_fs_ prefix is used to indicate the fn is used by both the
- * securityfs and apparmorfs filesystems.
- */
- /*
- * support fns
- */
- /**
- * aa_mangle_name - mangle a profile name to std profile layout form
- * @name: profile name to mangle (NOT NULL)
- * @target: buffer to store mangled name, same length as @name (MAYBE NULL)
- *
- * Returns: length of mangled name
- */
- static int mangle_name(const char *name, char *target)
- {
- char *t = target;
- while (*name == '/' || *name == '.')
- name++;
- if (target) {
- for (; *name; name++) {
- if (*name == '/')
- *(t)++ = '.';
- else if (isspace(*name))
- *(t)++ = '_';
- else if (isalnum(*name) || strchr("._-", *name))
- *(t)++ = *name;
- }
- *t = 0;
- } else {
- int len = 0;
- for (; *name; name++) {
- if (isalnum(*name) || isspace(*name) ||
- strchr("/._-", *name))
- len++;
- }
- return len;
- }
- return t - target;
- }
- /*
- * aafs - core fns and data for the policy tree
- */
- #define AAFS_NAME "apparmorfs"
- static struct vfsmount *aafs_mnt;
- static int aafs_count;
- static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
- {
- seq_printf(seq, "%s:[%lu]", AAFS_NAME, d_inode(dentry)->i_ino);
- return 0;
- }
- static void aafs_i_callback(struct rcu_head *head)
- {
- struct inode *inode = container_of(head, struct inode, i_rcu);
- if (S_ISLNK(inode->i_mode))
- kfree(inode->i_link);
- free_inode_nonrcu(inode);
- }
- static void aafs_destroy_inode(struct inode *inode)
- {
- call_rcu(&inode->i_rcu, aafs_i_callback);
- }
- static const struct super_operations aafs_super_ops = {
- .statfs = simple_statfs,
- .destroy_inode = aafs_destroy_inode,
- .show_path = aafs_show_path,
- };
- static int fill_super(struct super_block *sb, void *data, int silent)
- {
- static struct tree_descr files[] = { {""} };
- int error;
- error = simple_fill_super(sb, AAFS_MAGIC, files);
- if (error)
- return error;
- sb->s_op = &aafs_super_ops;
- return 0;
- }
- static struct dentry *aafs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
- {
- return mount_single(fs_type, flags, data, fill_super);
- }
- static struct file_system_type aafs_ops = {
- .owner = THIS_MODULE,
- .name = AAFS_NAME,
- .mount = aafs_mount,
- .kill_sb = kill_anon_super,
- };
- /**
- * __aafs_setup_d_inode - basic inode setup for apparmorfs
- * @dir: parent directory for the dentry
- * @dentry: dentry we are seting the inode up for
- * @mode: permissions the file should have
- * @data: data to store on inode.i_private, available in open()
- * @link: if symlink, symlink target string
- * @fops: struct file_operations that should be used
- * @iops: struct of inode_operations that should be used
- */
- static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
- umode_t mode, void *data, char *link,
- const struct file_operations *fops,
- const struct inode_operations *iops)
- {
- struct inode *inode = new_inode(dir->i_sb);
- AA_BUG(!dir);
- AA_BUG(!dentry);
- if (!inode)
- return -ENOMEM;
- inode->i_ino = get_next_ino();
- inode->i_mode = mode;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
- inode->i_private = data;
- if (S_ISDIR(mode)) {
- inode->i_op = iops ? iops : &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
- inc_nlink(inode);
- inc_nlink(dir);
- } else if (S_ISLNK(mode)) {
- inode->i_op = iops ? iops : &simple_symlink_inode_operations;
- inode->i_link = link;
- } else {
- inode->i_fop = fops;
- }
- d_instantiate(dentry, inode);
- dget(dentry);
- return 0;
- }
- /**
- * aafs_create - create a dentry in the apparmorfs filesystem
- *
- * @name: name of dentry to create
- * @mode: permissions the file should have
- * @parent: parent directory for this dentry
- * @data: data to store on inode.i_private, available in open()
- * @link: if symlink, symlink target string
- * @fops: struct file_operations that should be used for
- * @iops: struct of inode_operations that should be used
- *
- * This is the basic "create a xxx" function for apparmorfs.
- *
- * Returns a pointer to a dentry if it succeeds, that must be free with
- * aafs_remove(). Will return ERR_PTR on failure.
- */
- static struct dentry *aafs_create(const char *name, umode_t mode,
- struct dentry *parent, void *data, void *link,
- const struct file_operations *fops,
- const struct inode_operations *iops)
- {
- struct dentry *dentry;
- struct inode *dir;
- int error;
- AA_BUG(!name);
- AA_BUG(!parent);
- if (!(mode & S_IFMT))
- mode = (mode & S_IALLUGO) | S_IFREG;
- error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
- if (error)
- return ERR_PTR(error);
- dir = d_inode(parent);
- inode_lock(dir);
- dentry = lookup_one_len(name, parent, strlen(name));
- if (IS_ERR(dentry)) {
- error = PTR_ERR(dentry);
- goto fail_lock;
- }
- if (d_really_is_positive(dentry)) {
- error = -EEXIST;
- goto fail_dentry;
- }
- error = __aafs_setup_d_inode(dir, dentry, mode, data, link, fops, iops);
- if (error)
- goto fail_dentry;
- inode_unlock(dir);
- return dentry;
- fail_dentry:
- dput(dentry);
- fail_lock:
- inode_unlock(dir);
- simple_release_fs(&aafs_mnt, &aafs_count);
- return ERR_PTR(error);
- }
- /**
- * aafs_create_file - create a file in the apparmorfs filesystem
- *
- * @name: name of dentry to create
- * @mode: permissions the file should have
- * @parent: parent directory for this dentry
- * @data: data to store on inode.i_private, available in open()
- * @fops: struct file_operations that should be used for
- *
- * see aafs_create
- */
- static struct dentry *aafs_create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops)
- {
- return aafs_create(name, mode, parent, data, NULL, fops, NULL);
- }
- /**
- * aafs_create_dir - create a directory in the apparmorfs filesystem
- *
- * @name: name of dentry to create
- * @parent: parent directory for this dentry
- *
- * see aafs_create
- */
- static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
- {
- return aafs_create(name, S_IFDIR | 0755, parent, NULL, NULL, NULL,
- NULL);
- }
- /**
- * aafs_create_symlink - create a symlink in the apparmorfs filesystem
- * @name: name of dentry to create
- * @parent: parent directory for this dentry
- * @target: if symlink, symlink target string
- * @private: private data
- * @iops: struct of inode_operations that should be used
- *
- * If @target parameter is %NULL, then the @iops parameter needs to be
- * setup to handle .readlink and .get_link inode_operations.
- */
- static struct dentry *aafs_create_symlink(const char *name,
- struct dentry *parent,
- const char *target,
- void *private,
- const struct inode_operations *iops)
- {
- struct dentry *dent;
- char *link = NULL;
- if (target) {
- if (!link)
- return ERR_PTR(-ENOMEM);
- }
- dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL,
- iops);
- if (IS_ERR(dent))
- kfree(link);
- return dent;
- }
- /**
- * aafs_remove - removes a file or directory from the apparmorfs filesystem
- *
- * @dentry: dentry of the file/directory/symlink to removed.
- */
- static void aafs_remove(struct dentry *dentry)
- {
- struct inode *dir;
- if (!dentry || IS_ERR(dentry))
- return;
- dir = d_inode(dentry->d_parent);
- inode_lock(dir);
- if (simple_positive(dentry)) {
- if (d_is_dir(dentry))
- simple_rmdir(dir, dentry);
- else
- simple_unlink(dir, dentry);
- d_delete(dentry);
- dput(dentry);
- }
- inode_unlock(dir);
- simple_release_fs(&aafs_mnt, &aafs_count);
- }
- /*
- * aa_fs - policy load/replace/remove
- */
- /**
- * aa_simple_write_to_buffer - common routine for getting policy from user
- * @userbuf: user buffer to copy data from (NOT NULL)
- * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
- * @copy_size: size of data to copy from user buffer
- * @pos: position write is at in the file (NOT NULL)
- *
- * Returns: kernel buffer containing copy of user buffer data or an
- * ERR_PTR on failure.
- */
- static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
- size_t alloc_size,
- size_t copy_size,
- loff_t *pos)
- {
- struct aa_loaddata *data;
- AA_BUG(copy_size > alloc_size);
- if (*pos != 0)
- /* only writes from pos 0, that is complete writes */
- return ERR_PTR(-ESPIPE);
- /* freed by caller to simple_write_to_buffer */
- data = aa_loaddata_alloc(alloc_size);
- if (IS_ERR(data))
- return data;
- data->size = copy_size;
- if (copy_from_user(data->data, userbuf, copy_size)) {
- kvfree(data);
- return ERR_PTR(-EFAULT);
- }
- return data;
- }
- static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
- loff_t *pos, struct aa_ns *ns)
- {
- struct aa_loaddata *data;
- struct aa_label *label;
- ssize_t error;
- label = begin_current_label_crit_section();
- /* high level check about policy management - fine grained in
- * below after unpack
- */
- error = aa_may_manage_policy(label, ns, mask);
- if (error)
- return error;
- data = aa_simple_write_to_buffer(buf, size, size, pos);
- error = PTR_ERR(data);
- if (!IS_ERR(data)) {
- error = aa_replace_profiles(ns, label, mask, data);
- aa_put_loaddata(data);
- }
- end_current_label_crit_section(label);
- return error;
- }
- /* .load file hook fn to load policy */
- static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
- loff_t *pos)
- {
- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns);
- aa_put_ns(ns);
- return error;
- }
- static const struct file_operations aa_fs_profile_load = {
- .write = profile_load,
- .llseek = default_llseek,
- };
- /* .replace file hook fn to load and/or replace policy */
- static ssize_t profile_replace(struct file *f, const char __user *buf,
- size_t size, loff_t *pos)
- {
- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
- buf, size, pos, ns);
- aa_put_ns(ns);
- return error;
- }
- static const struct file_operations aa_fs_profile_replace = {
- .write = profile_replace,
- .llseek = default_llseek,
- };
- /* .remove file hook fn to remove loaded policy */
- static ssize_t profile_remove(struct file *f, const char __user *buf,
- size_t size, loff_t *pos)
- {
- struct aa_loaddata *data;
- struct aa_label *label;
- ssize_t error;
- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- label = begin_current_label_crit_section();
- /* high level check about policy management - fine grained in
- * below after unpack
- */
- error = aa_may_manage_policy(label, ns, AA_MAY_REMOVE_POLICY);
- if (error)
- goto out;
- /*
- * aa_remove_profile needs a null terminated string so 1 extra
- * byte is allocated and the copied data is null terminated.
- */
- data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
- error = PTR_ERR(data);
- if (!IS_ERR(data)) {
- data->data[size] = 0;
- error = aa_remove_profiles(ns, label, data->data, size);
- aa_put_loaddata(data);
- }
- out:
- end_current_label_crit_section(label);
- aa_put_ns(ns);
- return error;
- }
- static const struct file_operations aa_fs_profile_remove = {
- .write = profile_remove,
- .llseek = default_llseek,
- };
- struct aa_revision {
- struct aa_ns *ns;
- long last_read;
- };
- /* revision file hook fn for policy loads */
- static int ns_revision_release(struct inode *inode, struct file *file)
- {
- struct aa_revision *rev = file->private_data;
- if (rev) {
- aa_put_ns(rev->ns);
- kfree(rev);
- }
- return 0;
- }
- static ssize_t ns_revision_read(struct file *file, char __user *buf,
- size_t size, loff_t *ppos)
- {
- struct aa_revision *rev = file->private_data;
- char buffer[32];
- long last_read;
- int avail;
- mutex_lock_nested(&rev->ns->lock, rev->ns->level);
- last_read = rev->last_read;
- if (last_read == rev->ns->revision) {
- mutex_unlock(&rev->ns->lock);
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- if (wait_event_interruptible(rev->ns->wait,
- last_read !=
- READ_ONCE(rev->ns->revision)))
- return -ERESTARTSYS;
- mutex_lock_nested(&rev->ns->lock, rev->ns->level);
- }
- avail = sprintf(buffer, "%ld\n", rev->ns->revision);
- if (*ppos + size > avail) {
- rev->last_read = rev->ns->revision;
- *ppos = 0;
- }
- mutex_unlock(&rev->ns->lock);
- return simple_read_from_buffer(buf, size, ppos, buffer, avail);
- }
- static int ns_revision_open(struct inode *inode, struct file *file)
- {
- struct aa_revision *rev = kzalloc(sizeof(*rev), GFP_KERNEL);
- if (!rev)
- return -ENOMEM;
- rev->ns = aa_get_ns(inode->i_private);
- if (!rev->ns)
- rev->ns = aa_get_current_ns();
- file->private_data = rev;
- return 0;
- }
- static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
- {
- struct aa_revision *rev = file->private_data;
- __poll_t mask = 0;
- if (rev) {
- mutex_lock_nested(&rev->ns->lock, rev->ns->level);
- poll_wait(file, &rev->ns->wait, pt);
- if (rev->last_read < rev->ns->revision)
- mask |= EPOLLIN | EPOLLRDNORM;
- mutex_unlock(&rev->ns->lock);
- }
- return mask;
- }
- void __aa_bump_ns_revision(struct aa_ns *ns)
- {
- WRITE_ONCE(ns->revision, ns->revision + 1);
- wake_up_interruptible(&ns->wait);
- }
- static const struct file_operations aa_fs_ns_revision_fops = {
- .owner = THIS_MODULE,
- .open = ns_revision_open,
- .poll = ns_revision_poll,
- .read = ns_revision_read,
- .llseek = generic_file_llseek,
- .release = ns_revision_release,
- };
- static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
- const char *match_str, size_t match_len)
- {
- struct aa_perms tmp = { };
- struct aa_dfa *dfa;
- unsigned int state = 0;
- if (profile_unconfined(profile))
- return;
- if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
- dfa = profile->file.dfa;
- state = aa_dfa_match_len(dfa, profile->file.start,
- match_str + 1, match_len - 1);
- if (state) {
- struct path_cond cond = { };
- tmp = aa_compute_fperms(dfa, state, &cond);
- }
- } else if (profile->policy.dfa) {
- if (!PROFILE_MEDIATES(profile, *match_str))
- return; /* no change to current perms */
- dfa = profile->policy.dfa;
- state = aa_dfa_match_len(dfa, profile->policy.start[0],
- match_str, match_len);
- if (state)
- aa_compute_perms(dfa, state, &tmp);
- }
- aa_apply_modes_to_perms(profile, &tmp);
- aa_perms_accum_raw(perms, &tmp);
- }
- /**
- * query_data - queries a policy and writes its data to buf
- * @buf: the resulting data is stored here (NOT NULL)
- * @buf_len: size of buf
- * @query: query string used to retrieve data
- * @query_len: size of query including second NUL byte
- *
- * The buffers pointed to by buf and query may overlap. The query buffer is
- * parsed before buf is written to.
- *
- * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
- * the security confinement context and <KEY> is the name of the data to
- * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
- *
- * Don't expect the contents of buf to be preserved on failure.
- *
- * Returns: number of characters written to buf or -errno on failure
- */
- static ssize_t query_data(char *buf, size_t buf_len,
- char *query, size_t query_len)
- {
- char *out;
- const char *key;
- struct label_it i;
- struct aa_label *label, *curr;
- struct aa_profile *profile;
- struct aa_data *data;
- u32 bytes, blocks;
- __le32 outle32;
- if (!query_len)
- return -EINVAL; /* need a query */
- key = query + strnlen(query, query_len) + 1;
- if (key + 1 >= query + query_len)
- return -EINVAL; /* not enough space for a non-empty key */
- if (key + strnlen(key, query + query_len - key) >= query + query_len)
- return -EINVAL; /* must end with NUL */
- if (buf_len < sizeof(bytes) + sizeof(blocks))
- return -EINVAL; /* not enough space */
- curr = begin_current_label_crit_section();
- label = aa_label_parse(curr, query, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
- if (IS_ERR(label))
- return PTR_ERR(label);
- /* We are going to leave space for two numbers. The first is the total
- * number of bytes we are writing after the first number. This is so
- * users can read the full output without reallocation.
- *
- * The second number is the number of data blocks we're writing. An
- * application might be confined by multiple policies having data in
- * the same key.
- */
- memset(buf, 0, sizeof(bytes) + sizeof(blocks));
- out = buf + sizeof(bytes) + sizeof(blocks);
- blocks = 0;
- label_for_each_confined(i, label, profile) {
- if (!profile->data)
- continue;
- data = rhashtable_lookup_fast(profile->data, &key,
- profile->data->p);
- if (data) {
- if (out + sizeof(outle32) + data->size > buf +
- buf_len) {
- aa_put_label(label);
- return -EINVAL; /* not enough space */
- }
- outle32 = __cpu_to_le32(data->size);
- memcpy(out, &outle32, sizeof(outle32));
- out += sizeof(outle32);
- memcpy(out, data->data, data->size);
- out += data->size;
- blocks++;
- }
- }
- aa_put_label(label);
- outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
- memcpy(buf, &outle32, sizeof(outle32));
- outle32 = __cpu_to_le32(blocks);
- memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
- return out - buf;
- }
- /**
- * query_label - queries a label and writes permissions to buf
- * @buf: the resulting permissions string is stored here (NOT NULL)
- * @buf_len: size of buf
- * @query: binary query string to match against the dfa
- * @query_len: size of query
- * @view_only: only compute for querier's view
- *
- * The buffers pointed to by buf and query may overlap. The query buffer is
- * parsed before buf is written to.
- *
- * The query should look like "LABEL_NAME\0DFA_STRING" where LABEL_NAME is
- * the name of the label, in the current namespace, that is to be queried and
- * DFA_STRING is a binary string to match against the label(s)'s DFA.
- *
- * LABEL_NAME must be NUL terminated. DFA_STRING may contain NUL characters
- * but must *not* be NUL terminated.
- *
- * Returns: number of characters written to buf or -errno on failure
- */
- static ssize_t query_label(char *buf, size_t buf_len,
- char *query, size_t query_len, bool view_only)
- {
- struct aa_profile *profile;
- struct aa_label *label, *curr;
- char *label_name, *match_str;
- size_t label_name_len, match_len;
- struct aa_perms perms;
- struct label_it i;
- if (!query_len)
- return -EINVAL;
- label_name = query;
- label_name_len = strnlen(query, query_len);
- if (!label_name_len || label_name_len == query_len)
- return -EINVAL;
- /**
- * The extra byte is to account for the null byte between the
- * profile name and dfa string. profile_name_len is greater
- * than zero and less than query_len, so a byte can be safely
- * added or subtracted.
- */
- match_str = label_name + label_name_len + 1;
- match_len = query_len - label_name_len - 1;
- curr = begin_current_label_crit_section();
- label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
- if (IS_ERR(label))
- return PTR_ERR(label);
- perms = allperms;
- if (view_only) {
- label_for_each_in_ns(i, labels_ns(label), label, profile) {
- profile_query_cb(profile, &perms, match_str, match_len);
- }
- } else {
- label_for_each(i, label, profile) {
- profile_query_cb(profile, &perms, match_str, match_len);
- }
- }
- aa_put_label(label);
- return scnprintf(buf, buf_len,
- "allow 0x%08x\ndeny 0x%08x\naudit 0x%08x\nquiet 0x%08x\n",
- perms.allow, perms.deny, perms.audit, perms.quiet);
- }
- /*
- * Transaction based IO.
- * The file expects a write which triggers the transaction, and then
- * possibly a read(s) which collects the result - which is stored in a
- * file-local buffer. Once a new write is performed, a new set of results
- * are stored in the file-local buffer.
- */
- struct multi_transaction {
- struct kref count;
- ssize_t size;
- char data[0];
- };
- #define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction))
- /* TODO: replace with per file lock */
- static DEFINE_SPINLOCK(multi_transaction_lock);
- static void multi_transaction_kref(struct kref *kref)
- {
- struct multi_transaction *t;
- t = container_of(kref, struct multi_transaction, count);
- free_page((unsigned long) t);
- }
- static struct multi_transaction *
- get_multi_transaction(struct multi_transaction *t)
- {
- if (t)
- kref_get(&(t->count));
- return t;
- }
- static void put_multi_transaction(struct multi_transaction *t)
- {
- if (t)
- kref_put(&(t->count), multi_transaction_kref);
- }
- /* does not increment @new's count */
- static void multi_transaction_set(struct file *file,
- struct multi_transaction *new, size_t n)
- {
- struct multi_transaction *old;
- AA_BUG(n > MULTI_TRANSACTION_LIMIT);
- new->size = n;
- spin_lock(&multi_transaction_lock);
- old = (struct multi_transaction *) file->private_data;
- file->private_data = new;
- spin_unlock(&multi_transaction_lock);
- put_multi_transaction(old);
- }
- static struct multi_transaction *multi_transaction_new(struct file *file,
- const char __user *buf,
- size_t size)
- {
- struct multi_transaction *t;
- if (size > MULTI_TRANSACTION_LIMIT - 1)
- return ERR_PTR(-EFBIG);
- t = (struct multi_transaction *)get_zeroed_page(GFP_KERNEL);
- if (!t)
- return ERR_PTR(-ENOMEM);
- kref_init(&t->count);
- if (copy_from_user(t->data, buf, size))
- return ERR_PTR(-EFAULT);
- return t;
- }
- static ssize_t multi_transaction_read(struct file *file, char __user *buf,
- size_t size, loff_t *pos)
- {
- struct multi_transaction *t;
- ssize_t ret;
- spin_lock(&multi_transaction_lock);
- t = get_multi_transaction(file->private_data);
- spin_unlock(&multi_transaction_lock);
- if (!t)
- return 0;
- ret = simple_read_from_buffer(buf, size, pos, t->data, t->size);
- put_multi_transaction(t);
- return ret;
- }
- static int multi_transaction_release(struct inode *inode, struct file *file)
- {
- put_multi_transaction(file->private_data);
- return 0;
- }
- #define QUERY_CMD_LABEL "label\0"
- #define QUERY_CMD_LABEL_LEN 6
- #define QUERY_CMD_PROFILE "profile\0"
- #define QUERY_CMD_PROFILE_LEN 8
- #define QUERY_CMD_LABELALL "labelall\0"
- #define QUERY_CMD_LABELALL_LEN 9
- #define QUERY_CMD_DATA "data\0"
- #define QUERY_CMD_DATA_LEN 5
- /**
- * aa_write_access - generic permissions and data query
- * @file: pointer to open apparmorfs/access file
- * @ubuf: user buffer containing the complete query string (NOT NULL)
- * @count: size of ubuf
- * @ppos: position in the file (MUST BE ZERO)
- *
- * Allows for one permissions or data query per open(), write(), and read()
- * sequence. The only queries currently supported are label-based queries for
- * permissions or data.
- *
- * For permissions queries, ubuf must begin with "label\0", followed by the
- * profile query specific format described in the query_label() function
- * documentation.
- *
- * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
- * <LABEL> is the name of the security confinement context and <KEY> is the
- * name of the data to retrieve.
- *
- * Returns: number of bytes written or -errno on failure
- */
- static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- struct multi_transaction *t;
- ssize_t len;
- if (*ppos)
- return -ESPIPE;
- t = multi_transaction_new(file, ubuf, count);
- if (IS_ERR(t))
- return PTR_ERR(t);
- if (count > QUERY_CMD_PROFILE_LEN &&
- !memcmp(t->data, QUERY_CMD_PROFILE, QUERY_CMD_PROFILE_LEN)) {
- len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
- t->data + QUERY_CMD_PROFILE_LEN,
- count - QUERY_CMD_PROFILE_LEN, true);
- } else if (count > QUERY_CMD_LABEL_LEN &&
- !memcmp(t->data, QUERY_CMD_LABEL, QUERY_CMD_LABEL_LEN)) {
- len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
- t->data + QUERY_CMD_LABEL_LEN,
- count - QUERY_CMD_LABEL_LEN, true);
- } else if (count > QUERY_CMD_LABELALL_LEN &&
- !memcmp(t->data, QUERY_CMD_LABELALL,
- QUERY_CMD_LABELALL_LEN)) {
- len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
- t->data + QUERY_CMD_LABELALL_LEN,
- count - QUERY_CMD_LABELALL_LEN, false);
- } else if (count > QUERY_CMD_DATA_LEN &&
- !memcmp(t->data, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
- len = query_data(t->data, MULTI_TRANSACTION_LIMIT,
- t->data + QUERY_CMD_DATA_LEN,
- count - QUERY_CMD_DATA_LEN);
- } else
- len = -EINVAL;
- if (len < 0) {
- put_multi_transaction(t);
- return len;
- }
- multi_transaction_set(file, t, len);
- return count;
- }
- static const struct file_operations aa_sfs_access = {
- .write = aa_write_access,
- .read = multi_transaction_read,
- .release = multi_transaction_release,
- .llseek = generic_file_llseek,
- };
- static int aa_sfs_seq_show(struct seq_file *seq, void *v)
- {
- struct aa_sfs_entry *fs_file = seq->private;
- if (!fs_file)
- return 0;
- switch (fs_file->v_type) {
- case AA_SFS_TYPE_BOOLEAN:
- seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no");
- break;
- case AA_SFS_TYPE_STRING:
- seq_printf(seq, "%s\n", fs_file->v.string);
- break;
- case AA_SFS_TYPE_U64:
- seq_printf(seq, "%#08lx\n", fs_file->v.u64);
- break;
- default:
- /* Ignore unpritable entry types. */
- break;
- }
- return 0;
- }
- static int aa_sfs_seq_open(struct inode *inode, struct file *file)
- {
- return single_open(file, aa_sfs_seq_show, inode->i_private);
- }
- const struct file_operations aa_sfs_seq_file_ops = {
- .owner = THIS_MODULE,
- .open = aa_sfs_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- /*
- * profile based file operations
- * policy/profiles/XXXX/profiles/ *
- */
- #define SEQ_PROFILE_FOPS(NAME) \
- static int seq_profile_ ##NAME ##_open(struct inode *inode, struct file *file)\
- { \
- return seq_profile_open(inode, file, seq_profile_ ##NAME ##_show); \
- } \
- \
- static const struct file_operations seq_profile_ ##NAME ##_fops = { \
- .owner = THIS_MODULE, \
- .open = seq_profile_ ##NAME ##_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = seq_profile_release, \
- } \
- static int seq_profile_open(struct inode *inode, struct file *file,
- int (*show)(struct seq_file *, void *))
- {
- struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
- int error = single_open(file, show, proxy);
- if (error) {
- file->private_data = NULL;
- aa_put_proxy(proxy);
- }
- return error;
- }
- static int seq_profile_release(struct inode *inode, struct file *file)
- {
- struct seq_file *seq = (struct seq_file *) file->private_data;
- if (seq)
- aa_put_proxy(seq->private);
- return single_release(inode, file);
- }
- static int seq_profile_name_show(struct seq_file *seq, void *v)
- {
- struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
- seq_printf(seq, "%s\n", profile->base.name);
- aa_put_label(label);
- return 0;
- }
- static int seq_profile_mode_show(struct seq_file *seq, void *v)
- {
- struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
- seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
- aa_put_label(label);
- return 0;
- }
- static int seq_profile_attach_show(struct seq_file *seq, void *v)
- {
- struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
- if (profile->attach)
- seq_printf(seq, "%s\n", profile->attach);
- else if (profile->xmatch)
- seq_puts(seq, "<unknown>\n");
- else
- seq_printf(seq, "%s\n", profile->base.name);
- aa_put_label(label);
- return 0;
- }
- static int seq_profile_hash_show(struct seq_file *seq, void *v)
- {
- struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
- unsigned int i, size = aa_hash_size();
- if (profile->hash) {
- for (i = 0; i < size; i++)
- seq_printf(seq, "%.2x", profile->hash[i]);
- seq_putc(seq, '\n');
- }
- aa_put_label(label);
- return 0;
- }
- SEQ_PROFILE_FOPS(name);
- SEQ_PROFILE_FOPS(mode);
- SEQ_PROFILE_FOPS(attach);
- SEQ_PROFILE_FOPS(hash);
- /*
- * namespace based files
- * several root files and
- * policy/ *
- */
- #define SEQ_NS_FOPS(NAME) \
- static int seq_ns_ ##NAME ##_open(struct inode *inode, struct file *file) \
- { \
- return single_open(file, seq_ns_ ##NAME ##_show, inode->i_private); \
- } \
- \
- static const struct file_operations seq_ns_ ##NAME ##_fops = { \
- .owner = THIS_MODULE, \
- .open = seq_ns_ ##NAME ##_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
- } \
- static int seq_ns_stacked_show(struct seq_file *seq, void *v)
- {
- struct aa_label *label;
- label = begin_current_label_crit_section();
- seq_printf(seq, "%s\n", label->size > 1 ? "yes" : "no");
- end_current_label_crit_section(label);
- return 0;
- }
- static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
- {
- struct aa_label *label;
- struct aa_profile *profile;
- struct label_it it;
- int count = 1;
- label = begin_current_label_crit_section();
- if (label->size > 1) {
- label_for_each(it, label, profile)
- if (profile->ns != labels_ns(label)) {
- count++;
- break;
- }
- }
- seq_printf(seq, "%s\n", count > 1 ? "yes" : "no");
- end_current_label_crit_section(label);
- return 0;
- }
- static int seq_ns_level_show(struct seq_file *seq, void *v)
- {
- struct aa_label *label;
- label = begin_current_label_crit_section();
- seq_printf(seq, "%d\n", labels_ns(label)->level);
- end_current_label_crit_section(label);
- return 0;
- }
- static int seq_ns_name_show(struct seq_file *seq, void *v)
- {
- struct aa_label *label = begin_current_label_crit_section();
- seq_printf(seq, "%s\n", labels_ns(label)->base.name);
- end_current_label_crit_section(label);
- return 0;
- }
- SEQ_NS_FOPS(stacked);
- SEQ_NS_FOPS(nsstacked);
- SEQ_NS_FOPS(level);
- SEQ_NS_FOPS(name);
- /* policy/raw_data/ * file ops */
- #define SEQ_RAWDATA_FOPS(NAME) \
- static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
- { \
- return seq_rawdata_open(inode, file, seq_rawdata_ ##NAME ##_show); \
- } \
- \
- static const struct file_operations seq_rawdata_ ##NAME ##_fops = { \
- .owner = THIS_MODULE, \
- .open = seq_rawdata_ ##NAME ##_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = seq_rawdata_release, \
- } \
- static int seq_rawdata_open(struct inode *inode, struct file *file,
- int (*show)(struct seq_file *, void *))
- {
- struct aa_loaddata *data = __aa_get_loaddata(inode->i_private);
- int error;
- if (!data)
- /* lost race this ent is being reaped */
- return -ENOENT;
- error = single_open(file, show, data);
- if (error) {
- AA_BUG(file->private_data &&
- ((struct seq_file *)file->private_data)->private);
- aa_put_loaddata(data);
- }
- return error;
- }
- static int seq_rawdata_release(struct inode *inode, struct file *file)
- {
- struct seq_file *seq = (struct seq_file *) file->private_data;
- if (seq)
- aa_put_loaddata(seq->private);
- return single_release(inode, file);
- }
- static int seq_rawdata_abi_show(struct seq_file *seq, void *v)
- {
- struct aa_loaddata *data = seq->private;
- seq_printf(seq, "v%d\n", data->abi);
- return 0;
- }
- static int seq_rawdata_revision_show(struct seq_file *seq, void *v)
- {
- struct aa_loaddata *data = seq->private;
- seq_printf(seq, "%ld\n", data->revision);
- return 0;
- }
- static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
- {
- struct aa_loaddata *data = seq->private;
- unsigned int i, size = aa_hash_size();
- if (data->hash) {
- for (i = 0; i < size; i++)
- seq_printf(seq, "%.2x", data->hash[i]);
- seq_putc(seq, '\n');
- }
- return 0;
- }
- SEQ_RAWDATA_FOPS(abi);
- SEQ_RAWDATA_FOPS(revision);
- SEQ_RAWDATA_FOPS(hash);
- static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
- loff_t *ppos)
- {
- struct aa_loaddata *rawdata = file->private_data;
- return simple_read_from_buffer(buf, size, ppos, rawdata->data,
- rawdata->size);
- }
- static int rawdata_release(struct inode *inode, struct file *file)
- {
- aa_put_loaddata(file->private_data);
- return 0;
- }
- static int rawdata_open(struct inode *inode, struct file *file)
- {
- if (!policy_view_capable(NULL))
- return -EACCES;
- file->private_data = __aa_get_loaddata(inode->i_private);
- if (!file->private_data)
- /* lost race: this entry is being reaped */
- return -ENOENT;
- return 0;
- }
- static const struct file_operations rawdata_fops = {
- .open = rawdata_open,
- .read = rawdata_read,
- .llseek = generic_file_llseek,
- .release = rawdata_release,
- };
- static void remove_rawdata_dents(struct aa_loaddata *rawdata)
- {
- int i;
- for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) {
- if (!IS_ERR_OR_NULL(rawdata->dents[i])) {
- /* no refcounts on i_private */
- aafs_remove(rawdata->dents[i]);
- rawdata->dents[i] = NULL;
- }
- }
- }
- void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
- {
- AA_BUG(rawdata->ns && !mutex_is_locked(&rawdata->ns->lock));
- if (rawdata->ns) {
- remove_rawdata_dents(rawdata);
- list_del_init(&rawdata->list);
- aa_put_ns(rawdata->ns);
- rawdata->ns = NULL;
- }
- }
- int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
- {
- struct dentry *dent, *dir;
- AA_BUG(!ns);
- AA_BUG(!rawdata);
- AA_BUG(!mutex_is_locked(&ns->lock));
- AA_BUG(!ns_subdata_dir(ns));
- /*
- * just use ns revision dir was originally created at. This is
- * under ns->lock and if load is successful revision will be
- * bumped and is guaranteed to be unique
- */
- rawdata->name = kasprintf(GFP_KERNEL, "%ld", ns->revision);
- if (!rawdata->name)
- return -ENOMEM;
- dir = aafs_create_dir(rawdata->name, ns_subdata_dir(ns));
- if (IS_ERR(dir))
- /* ->name freed when rawdata freed */
- return PTR_ERR(dir);
- rawdata->dents[AAFS_LOADDATA_DIR] = dir;
- dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata,
- &seq_rawdata_abi_fops);
- if (IS_ERR(dent))
- goto fail;
- rawdata->dents[AAFS_LOADDATA_ABI] = dent;
- dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata,
- &seq_rawdata_revision_fops);
- if (IS_ERR(dent))
- goto fail;
- rawdata->dents[AAFS_LOADDATA_REVISION] = dent;
- if (aa_g_hash_policy) {
- dent = aafs_create_file("sha1", S_IFREG | 0444, dir,
- rawdata, &seq_rawdata_hash_fops);
- if (IS_ERR(dent))
- goto fail;
- rawdata->dents[AAFS_LOADDATA_HASH] = dent;
- }
- dent = aafs_create_file("raw_data", S_IFREG | 0444,
- dir, rawdata, &rawdata_fops);
- if (IS_ERR(dent))
- goto fail;
- rawdata->dents[AAFS_LOADDATA_DATA] = dent;
- d_inode(dent)->i_size = rawdata->size;
- rawdata->ns = aa_get_ns(ns);
- list_add(&rawdata->list, &ns->rawdata_list);
- /* no refcount on inode rawdata */
- return 0;
- fail:
- remove_rawdata_dents(rawdata);
- return PTR_ERR(dent);
- }
- /** fns to setup dynamic per profile/namespace files **/
- /**
- *
- * Requires: @profile->ns->lock held
- */
- void __aafs_profile_rmdir(struct aa_profile *profile)
- {
- struct aa_profile *child;
- int i;
- if (!profile)
- return;
- list_for_each_entry(child, &profile->base.profiles, base.list)
- __aafs_profile_rmdir(child);
- for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
- struct aa_proxy *proxy;
- if (!profile->dents[i])
- continue;
- proxy = d_inode(profile->dents[i])->i_private;
- aafs_remove(profile->dents[i]);
- aa_put_proxy(proxy);
- profile->dents[i] = NULL;
- }
- }
- /**
- *
- * Requires: @old->ns->lock held
- */
- void __aafs_profile_migrate_dents(struct aa_profile *old,
- struct aa_profile *new)
- {
- int i;
- AA_BUG(!old);
- AA_BUG(!new);
- AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
- for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
- new->dents[i] = old->dents[i];
- if (new->dents[i])
- new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode);
- old->dents[i] = NULL;
- }
- }
- static struct dentry *create_profile_file(struct dentry *dir, const char *name,
- struct aa_profile *profile,
- const struct file_operations *fops)
- {
- struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy);
- struct dentry *dent;
- dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
- if (IS_ERR(dent))
- aa_put_proxy(proxy);
- return dent;
- }
- static int profile_depth(struct aa_profile *profile)
- {
- int depth = 0;
- rcu_read_lock();
- for (depth = 0; profile; profile = rcu_access_pointer(profile->parent))
- depth++;
- rcu_read_unlock();
- return depth;
- }
- static char *gen_symlink_name(int depth, const char *dirname, const char *fname)
- {
- char *buffer, *s;
- int error;
- int size = depth * 6 + strlen(dirname) + strlen(fname) + 11;
- s = buffer = kmalloc(size, GFP_KERNEL);
- if (!buffer)
- return ERR_PTR(-ENOMEM);
- for (; depth > 0; depth--) {
- strcpy(s, "../../");
- s += 6;
- size -= 6;
- }
- error = snprintf(s, size, "raw_data/%s/%s", dirname, fname);
- if (error >= size || error < 0) {
- kfree(buffer);
- return ERR_PTR(-ENAMETOOLONG);
- }
- return buffer;
- }
- static void rawdata_link_cb(void *arg)
- {
- kfree(arg);
- }
- static const char *rawdata_get_link_base(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done,
- const char *name)
- {
- struct aa_proxy *proxy = inode->i_private;
- struct aa_label *label;
- struct aa_profile *profile;
- char *target;
- int depth;
- if (!dentry)
- return ERR_PTR(-ECHILD);
- label = aa_get_label_rcu(&proxy->label);
- profile = labels_profile(label);
- depth = profile_depth(profile);
- target = gen_symlink_name(depth, profile->rawdata->name, name);
- aa_put_label(label);
- if (IS_ERR(target))
- return target;
- set_delayed_call(done, rawdata_link_cb, target);
- return target;
- }
- static const char *rawdata_get_link_sha1(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
- {
- return rawdata_get_link_base(dentry, inode, done, "sha1");
- }
- static const char *rawdata_get_link_abi(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
- {
- return rawdata_get_link_base(dentry, inode, done, "abi");
- }
- static const char *rawdata_get_link_data(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
- {
- return rawdata_get_link_base(dentry, inode, done, "raw_data");
- }
- static const struct inode_operations rawdata_link_sha1_iops = {
- .get_link = rawdata_get_link_sha1,
- };
- static const struct inode_operations rawdata_link_abi_iops = {
- .get_link = rawdata_get_link_abi,
- };
- static const struct inode_operations rawdata_link_data_iops = {
- .get_link = rawdata_get_link_data,
- };
- /*
- * Requires: @profile->ns->lock held
- */
- int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
- {
- struct aa_profile *child;
- struct dentry *dent = NULL, *dir;
- int error;
- AA_BUG(!profile);
- AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
- if (!parent) {
- struct aa_profile *p;
- p = aa_deref_parent(profile);
- dent = prof_dir(p);
- /* adding to parent that previously didn't have children */
- dent = aafs_create_dir("profiles", dent);
- if (IS_ERR(dent))
- goto fail;
- prof_child_dir(p) = parent = dent;
- }
- if (!profile->dirname) {
- int len, id_len;
- len = mangle_name(profile->base.name, NULL);
- id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
- profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
- if (!profile->dirname) {
- error = -ENOMEM;
- goto fail2;
- }
- mangle_name(profile->base.name, profile->dirname);
- sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
- }
- dent = aafs_create_dir(profile->dirname, parent);
- if (IS_ERR(dent))
- goto fail;
- prof_dir(profile) = dir = dent;
- dent = create_profile_file(dir, "name", profile,
- &seq_profile_name_fops);
- if (IS_ERR(dent))
- goto fail;
- profile->dents[AAFS_PROF_NAME] = dent;
- dent = create_profile_file(dir, "mode", profile,
- &seq_profile_mode_fops);
- if (IS_ERR(dent))
- goto fail;
- profile->dents[AAFS_PROF_MODE] = dent;
- dent = create_profile_file(dir, "attach", profile,
- &seq_profile_attach_fops);
- if (IS_ERR(dent))
- goto fail;
- profile->dents[AAFS_PROF_ATTACH] = dent;
- if (profile->hash) {
- dent = create_profile_file(dir, "sha1", profile,
- &seq_profile_hash_fops);
- if (IS_ERR(dent))
- goto fail;
- profile->dents[AAFS_PROF_HASH] = dent;
- }
- if (profile->rawdata) {
- dent = aafs_create_symlink("raw_sha1", dir, NULL,
- profile->label.proxy,
- &rawdata_link_sha1_iops);
- if (IS_ERR(dent))
- goto fail;
- aa_get_proxy(profile->label.proxy);
- profile->dents[AAFS_PROF_RAW_HASH] = dent;
- dent = aafs_create_symlink("raw_abi", dir, NULL,
- profile->label.proxy,
- &rawdata_link_abi_iops);
- if (IS_ERR(dent))
- goto fail;
- aa_get_proxy(profile->label.proxy);
- profile->dents[AAFS_PROF_RAW_ABI] = dent;
- dent = aafs_create_symlink("raw_data", dir, NULL,
- profile->label.proxy,
- &rawdata_link_data_iops);
- if (IS_ERR(dent))
- goto fail;
- aa_get_proxy(profile->label.proxy);
- profile->dents[AAFS_PROF_RAW_DATA] = dent;
- }
- list_for_each_entry(child, &profile->base.profiles, base.list) {
- error = __aafs_profile_mkdir(child, prof_child_dir(profile));
- if (error)
- goto fail2;
- }
- return 0;
- fail:
- error = PTR_ERR(dent);
- fail2:
- __aafs_profile_rmdir(profile);
- return error;
- }
- static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
- {
- struct aa_ns *ns, *parent;
- /* TODO: improve permission check */
- struct aa_label *label;
- int error;
- label = begin_current_label_crit_section();
- error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
- if (error)
- return error;
- parent = aa_get_ns(dir->i_private);
- AA_BUG(d_inode(ns_subns_dir(parent)) != dir);
- /* we have to unlock and then relock to get locking order right
- * for pin_fs
- */
- inode_unlock(dir);
- error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
- mutex_lock_nested(&parent->lock, parent->level);
- inode_lock_nested(dir, I_MUTEX_PARENT);
- if (error)
- goto out;
- error = __aafs_setup_d_inode(dir, dentry, mode | S_IFDIR, NULL,
- NULL, NULL, NULL);
- if (error)
- goto out_pin;
- ns = __aa_find_or_create_ns(parent, READ_ONCE(dentry->d_name.name),
- dentry);
- if (IS_ERR(ns)) {
- error = PTR_ERR(ns);
- ns = NULL;
- }
- aa_put_ns(ns); /* list ref remains */
- out_pin:
- if (error)
- simple_release_fs(&aafs_mnt, &aafs_count);
- out:
- mutex_unlock(&parent->lock);
- aa_put_ns(parent);
- return error;
- }
- static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
- {
- struct aa_ns *ns, *parent;
- /* TODO: improve permission check */
- struct aa_label *label;
- int error;
- label = begin_current_label_crit_section();
- error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
- if (error)
- return error;
- parent = aa_get_ns(dir->i_private);
- /* rmdir calls the generic securityfs functions to remove files
- * from the apparmor dir. It is up to the apparmor ns locking
- * to avoid races.
- */
- inode_unlock(dir);
- inode_unlock(dentry->d_inode);
- mutex_lock_nested(&parent->lock, parent->level);
- ns = aa_get_ns(__aa_findn_ns(&parent->sub_ns, dentry->d_name.name,
- dentry->d_name.len));
- if (!ns) {
- error = -ENOENT;
- goto out;
- }
- AA_BUG(ns_dir(ns) != dentry);
- __aa_remove_ns(ns);
- aa_put_ns(ns);
- out:
- mutex_unlock(&parent->lock);
- inode_lock_nested(dir, I_MUTEX_PARENT);
- inode_lock(dentry->d_inode);
- aa_put_ns(parent);
- return error;
- }
- static const struct inode_operations ns_dir_inode_operations = {
- .lookup = simple_lookup,
- .mkdir = ns_mkdir_op,
- .rmdir = ns_rmdir_op,
- };
- static void __aa_fs_list_remove_rawdata(struct aa_ns *ns)
- {
- struct aa_loaddata *ent, *tmp;
- AA_BUG(!mutex_is_locked(&ns->lock));
- list_for_each_entry_safe(ent, tmp, &ns->rawdata_list, list)
- __aa_fs_remove_rawdata(ent);
- }
- /**
- *
- * Requires: @ns->lock held
- */
- void __aafs_ns_rmdir(struct aa_ns *ns)
- {
- struct aa_ns *sub;
- struct aa_profile *child;
- int i;
- if (!ns)
- return;
- AA_BUG(!mutex_is_locked(&ns->lock));
- list_for_each_entry(child, &ns->base.profiles, base.list)
- __aafs_profile_rmdir(child);
- list_for_each_entry(sub, &ns->sub_ns, base.list) {
- mutex_lock_nested(&sub->lock, sub->level);
- __aafs_ns_rmdir(sub);
- mutex_unlock(&sub->lock);
- }
- __aa_fs_list_remove_rawdata(ns);
- if (ns_subns_dir(ns)) {
- sub = d_inode(ns_subns_dir(ns))->i_private;
- aa_put_ns(sub);
- }
- if (ns_subload(ns)) {
- sub = d_inode(ns_subload(ns))->i_private;
- aa_put_ns(sub);
- }
- if (ns_subreplace(ns)) {
- sub = d_inode(ns_subreplace(ns))->i_private;
- aa_put_ns(sub);
- }
- if (ns_subremove(ns)) {
- sub = d_inode(ns_subremove(ns))->i_private;
- aa_put_ns(sub);
- }
- if (ns_subrevision(ns)) {
- sub = d_inode(ns_subrevision(ns))->i_private;
- aa_put_ns(sub);
- }
- for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
- aafs_remove(ns->dents[i]);
- ns->dents[i] = NULL;
- }
- }
- /* assumes cleanup in caller */
- static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
- {
- struct dentry *dent;
- AA_BUG(!ns);
- AA_BUG(!dir);
- dent = aafs_create_dir("profiles", dir);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- ns_subprofs_dir(ns) = dent;
- dent = aafs_create_dir("raw_data", dir);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- ns_subdata_dir(ns) = dent;
- dent = aafs_create_file("revision", 0444, dir, ns,
- &aa_fs_ns_revision_fops);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subrevision(ns) = dent;
- dent = aafs_create_file(".load", 0640, dir, ns,
- &aa_fs_profile_load);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subload(ns) = dent;
- dent = aafs_create_file(".replace", 0640, dir, ns,
- &aa_fs_profile_replace);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subreplace(ns) = dent;
- dent = aafs_create_file(".remove", 0640, dir, ns,
- &aa_fs_profile_remove);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subremove(ns) = dent;
- /* use create_dentry so we can supply private data */
- dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL,
- &ns_dir_inode_operations);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subns_dir(ns) = dent;
- return 0;
- }
- /*
- * Requires: @ns->lock held
- */
- int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
- struct dentry *dent)
- {
- struct aa_ns *sub;
- struct aa_profile *child;
- struct dentry *dir;
- int error;
- AA_BUG(!ns);
- AA_BUG(!parent);
- AA_BUG(!mutex_is_locked(&ns->lock));
- if (!name)
- name = ns->base.name;
- if (!dent) {
- /* create ns dir if it doesn't already exist */
- dent = aafs_create_dir(name, parent);
- if (IS_ERR(dent))
- goto fail;
- } else
- dget(dent);
- ns_dir(ns) = dir = dent;
- error = __aafs_ns_mkdir_entries(ns, dir);
- if (error)
- goto fail2;
- /* profiles */
- list_for_each_entry(child, &ns->base.profiles, base.list) {
- error = __aafs_profile_mkdir(child, ns_subprofs_dir(ns));
- if (error)
- goto fail2;
- }
- /* subnamespaces */
- list_for_each_entry(sub, &ns->sub_ns, base.list) {
- mutex_lock_nested(&sub->lock, sub->level);
- error = __aafs_ns_mkdir(sub, ns_subns_dir(ns), NULL, NULL);
- mutex_unlock(&sub->lock);
- if (error)
- goto fail2;
- }
- return 0;
- fail:
- error = PTR_ERR(dent);
- fail2:
- __aafs_ns_rmdir(ns);
- return error;
- }
- #define list_entry_is_head(pos, head, member) (&pos->member == (head))
- /**
- * __next_ns - find the next namespace to list
- * @root: root namespace to stop search at (NOT NULL)
- * @ns: current ns position (NOT NULL)
- *
- * Find the next namespace from @ns under @root and handle all locking needed
- * while switching current namespace.
- *
- * Returns: next namespace or NULL if at last namespace under @root
- * Requires: ns->parent->lock to be held
- * NOTE: will not unlock root->lock
- */
- static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
- {
- struct aa_ns *parent, *next;
- AA_BUG(!root);
- AA_BUG(!ns);
- AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
- /* is next namespace a child */
- if (!list_empty(&ns->sub_ns)) {
- next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
- mutex_lock_nested(&next->lock, next->level);
- return next;
- }
- /* check if the next ns is a sibling, parent, gp, .. */
- parent = ns->parent;
- while (ns != root) {
- mutex_unlock(&ns->lock);
- next = list_next_entry(ns, base.list);
- if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
- mutex_lock_nested(&next->lock, next->level);
- return next;
- }
- ns = parent;
- parent = parent->parent;
- }
- return NULL;
- }
- /**
- * __first_profile - find the first profile in a namespace
- * @root: namespace that is root of profiles being displayed (NOT NULL)
- * @ns: namespace to start in (NOT NULL)
- *
- * Returns: unrefcounted profile or NULL if no profile
- * Requires: profile->ns.lock to be held
- */
- static struct aa_profile *__first_profile(struct aa_ns *root,
- struct aa_ns *ns)
- {
- AA_BUG(!root);
- AA_BUG(ns && !mutex_is_locked(&ns->lock));
- for (; ns; ns = __next_ns(root, ns)) {
- if (!list_empty(&ns->base.profiles))
- return list_first_entry(&ns->base.profiles,
- struct aa_profile, base.list);
- }
- return NULL;
- }
- /**
- * __next_profile - step to the next profile in a profile tree
- * @profile: current profile in tree (NOT NULL)
- *
- * Perform a depth first traversal on the profile tree in a namespace
- *
- * Returns: next profile or NULL if done
- * Requires: profile->ns.lock to be held
- */
- static struct aa_profile *__next_profile(struct aa_profile *p)
- {
- struct aa_profile *parent;
- struct aa_ns *ns = p->ns;
- AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
- /* is next profile a child */
- if (!list_empty(&p->base.profiles))
- return list_first_entry(&p->base.profiles, typeof(*p),
- base.list);
- /* is next profile a sibling, parent sibling, gp, sibling, .. */
- parent = rcu_dereference_protected(p->parent,
- mutex_is_locked(&p->ns->lock));
- while (parent) {
- p = list_next_entry(p, base.list);
- if (!list_entry_is_head(p, &parent->base.profiles, base.list))
- return p;
- p = parent;
- parent = rcu_dereference_protected(parent->parent,
- mutex_is_locked(&parent->ns->lock));
- }
- /* is next another profile in the namespace */
- p = list_next_entry(p, base.list);
- if (!list_entry_is_head(p, &ns->base.profiles, base.list))
- return p;
- return NULL;
- }
- /**
- * next_profile - step to the next profile in where ever it may be
- * @root: root namespace (NOT NULL)
- * @profile: current profile (NOT NULL)
- *
- * Returns: next profile or NULL if there isn't one
- */
- static struct aa_profile *next_profile(struct aa_ns *root,
- struct aa_profile *profile)
- {
- struct aa_profile *next = __next_profile(profile);
- if (next)
- return next;
- /* finished all profiles in namespace move to next namespace */
- return __first_profile(root, __next_ns(root, profile->ns));
- }
- /**
- * p_start - start a depth first traversal of profile tree
- * @f: seq_file to fill
- * @pos: current position
- *
- * Returns: first profile under current namespace or NULL if none found
- *
- * acquires first ns->lock
- */
- static void *p_start(struct seq_file *f, loff_t *pos)
- {
- struct aa_profile *profile = NULL;
- struct aa_ns *root = aa_get_current_ns();
- loff_t l = *pos;
- f->private = root;
- /* find the first profile */
- mutex_lock_nested(&root->lock, root->level);
- profile = __first_profile(root, root);
- /* skip to position */
- for (; profile && l > 0; l--)
- profile = next_profile(root, profile);
- return profile;
- }
- /**
- * p_next - read the next profile entry
- * @f: seq_file to fill
- * @p: profile previously returned
- * @pos: current position
- *
- * Returns: next profile after @p or NULL if none
- *
- * may acquire/release locks in namespace tree as necessary
- */
- static void *p_next(struct seq_file *f, void *p, loff_t *pos)
- {
- struct aa_profile *profile = p;
- struct aa_ns *ns = f->private;
- (*pos)++;
- return next_profile(ns, profile);
- }
- /**
- * p_stop - stop depth first traversal
- * @f: seq_file we are filling
- * @p: the last profile writen
- *
- * Release all locking done by p_start/p_next on namespace tree
- */
- static void p_stop(struct seq_file *f, void *p)
- {
- struct aa_profile *profile = p;
- struct aa_ns *root = f->private, *ns;
- if (profile) {
- for (ns = profile->ns; ns && ns != root; ns = ns->parent)
- mutex_unlock(&ns->lock);
- }
- mutex_unlock(&root->lock);
- aa_put_ns(root);
- }
- /**
- * seq_show_profile - show a profile entry
- * @f: seq_file to file
- * @p: current position (profile) (NOT NULL)
- *
- * Returns: error on failure
- */
- static int seq_show_profile(struct seq_file *f, void *p)
- {
- struct aa_profile *profile = (struct aa_profile *)p;
- struct aa_ns *root = f->private;
- aa_label_seq_xprint(f, root, &profile->label,
- FLAG_SHOW_MODE | FLAG_VIEW_SUBNS, GFP_KERNEL);
- seq_putc(f, '\n');
- return 0;
- }
- static const struct seq_operations aa_sfs_profiles_op = {
- .start = p_start,
- .next = p_next,
- .stop = p_stop,
- .show = seq_show_profile,
- };
- static int profiles_open(struct inode *inode, struct file *file)
- {
- if (!policy_view_capable(NULL))
- return -EACCES;
- return seq_open(file, &aa_sfs_profiles_op);
- }
- static int profiles_release(struct inode *inode, struct file *file)
- {
- return seq_release(inode, file);
- }
- static const struct file_operations aa_sfs_profiles_fops = {
- .open = profiles_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = profiles_release,
- };
- /** Base file system setup **/
- static struct aa_sfs_entry aa_sfs_entry_file[] = {
- AA_SFS_FILE_STRING("mask",
- "create read write exec append mmap_exec link lock"),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
- AA_SFS_FILE_STRING("mask", "read trace"),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_signal[] = {
- AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_attach[] = {
- AA_SFS_FILE_BOOLEAN("xattr", 1),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_domain[] = {
- AA_SFS_FILE_BOOLEAN("change_hat", 1),
- AA_SFS_FILE_BOOLEAN("change_hatv", 1),
- AA_SFS_FILE_BOOLEAN("change_onexec", 1),
- AA_SFS_FILE_BOOLEAN("change_profile", 1),
- AA_SFS_FILE_BOOLEAN("stack", 1),
- AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
- AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1),
- AA_SFS_FILE_BOOLEAN("computed_longest_left", 1),
- AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach),
- AA_SFS_FILE_STRING("version", "1.2"),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_versions[] = {
- AA_SFS_FILE_BOOLEAN("v5", 1),
- AA_SFS_FILE_BOOLEAN("v6", 1),
- AA_SFS_FILE_BOOLEAN("v7", 1),
- AA_SFS_FILE_BOOLEAN("v8", 1),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_policy[] = {
- AA_SFS_DIR("versions", aa_sfs_entry_versions),
- AA_SFS_FILE_BOOLEAN("set_load", 1),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_mount[] = {
- AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_ns[] = {
- AA_SFS_FILE_BOOLEAN("profile", 1),
- AA_SFS_FILE_BOOLEAN("pivot_root", 0),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
- AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
- AA_SFS_FILE_BOOLEAN("data", 1),
- AA_SFS_FILE_BOOLEAN("multi_transaction", 1),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_query[] = {
- AA_SFS_DIR("label", aa_sfs_entry_query_label),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_features[] = {
- AA_SFS_DIR("policy", aa_sfs_entry_policy),
- AA_SFS_DIR("domain", aa_sfs_entry_domain),
- AA_SFS_DIR("file", aa_sfs_entry_file),
- AA_SFS_DIR("network_v8", aa_sfs_entry_network),
- AA_SFS_DIR("mount", aa_sfs_entry_mount),
- AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
- AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
- AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
- AA_SFS_DIR("caps", aa_sfs_entry_caps),
- AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
- AA_SFS_DIR("signal", aa_sfs_entry_signal),
- AA_SFS_DIR("query", aa_sfs_entry_query),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
- AA_SFS_FILE_FOPS(".access", 0666, &aa_sfs_access),
- AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops),
- AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops),
- AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
- AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
- AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
- AA_SFS_DIR("features", aa_sfs_entry_features),
- { }
- };
- static struct aa_sfs_entry aa_sfs_entry =
- AA_SFS_DIR("apparmor", aa_sfs_entry_apparmor);
- /**
- * entry_create_file - create a file entry in the apparmor securityfs
- * @fs_file: aa_sfs_entry to build an entry for (NOT NULL)
- * @parent: the parent dentry in the securityfs
- *
- * Use entry_remove_file to remove entries created with this fn.
- */
- static int __init entry_create_file(struct aa_sfs_entry *fs_file,
- struct dentry *parent)
- {
- int error = 0;
- fs_file->dentry = securityfs_create_file(fs_file->name,
- S_IFREG | fs_file->mode,
- parent, fs_file,
- fs_file->file_ops);
- if (IS_ERR(fs_file->dentry)) {
- error = PTR_ERR(fs_file->dentry);
- fs_file->dentry = NULL;
- }
- return error;
- }
- static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir);
- /**
- * entry_create_dir - recursively create a directory entry in the securityfs
- * @fs_dir: aa_sfs_entry (and all child entries) to build (NOT NULL)
- * @parent: the parent dentry in the securityfs
- *
- * Use entry_remove_dir to remove entries created with this fn.
- */
- static int __init entry_create_dir(struct aa_sfs_entry *fs_dir,
- struct dentry *parent)
- {
- struct aa_sfs_entry *fs_file;
- struct dentry *dir;
- int error;
- dir = securityfs_create_dir(fs_dir->name, parent);
- if (IS_ERR(dir))
- return PTR_ERR(dir);
- fs_dir->dentry = dir;
- for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
- if (fs_file->v_type == AA_SFS_TYPE_DIR)
- error = entry_create_dir(fs_file, fs_dir->dentry);
- else
- error = entry_create_file(fs_file, fs_dir->dentry);
- if (error)
- goto failed;
- }
- return 0;
- failed:
- entry_remove_dir(fs_dir);
- return error;
- }
- /**
- * entry_remove_file - drop a single file entry in the apparmor securityfs
- * @fs_file: aa_sfs_entry to detach from the securityfs (NOT NULL)
- */
- static void __init entry_remove_file(struct aa_sfs_entry *fs_file)
- {
- if (!fs_file->dentry)
- return;
- securityfs_remove(fs_file->dentry);
- fs_file->dentry = NULL;
- }
- /**
- * entry_remove_dir - recursively drop a directory entry from the securityfs
- * @fs_dir: aa_sfs_entry (and all child entries) to detach (NOT NULL)
- */
- static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir)
- {
- struct aa_sfs_entry *fs_file;
- for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
- if (fs_file->v_type == AA_SFS_TYPE_DIR)
- entry_remove_dir(fs_file);
- else
- entry_remove_file(fs_file);
- }
- entry_remove_file(fs_dir);
- }
- /**
- * aa_destroy_aafs - cleanup and free aafs
- *
- * releases dentries allocated by aa_create_aafs
- */
- void __init aa_destroy_aafs(void)
- {
- entry_remove_dir(&aa_sfs_entry);
- }
- #define NULL_FILE_NAME ".null"
- struct path aa_null;
- static int aa_mk_null_file(struct dentry *parent)
- {
- struct vfsmount *mount = NULL;
- struct dentry *dentry;
- struct inode *inode;
- int count = 0;
- int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
- if (error)
- return error;
- inode_lock(d_inode(parent));
- dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
- if (IS_ERR(dentry)) {
- error = PTR_ERR(dentry);
- goto out;
- }
- inode = new_inode(parent->d_inode->i_sb);
- if (!inode) {
- error = -ENOMEM;
- goto out1;
- }
- inode->i_ino = get_next_ino();
- inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
- init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
- MKDEV(MEM_MAJOR, 3));
- d_instantiate(dentry, inode);
- aa_null.dentry = dget(dentry);
- aa_null.mnt = mntget(mount);
- error = 0;
- out1:
- dput(dentry);
- out:
- inode_unlock(d_inode(parent));
- simple_release_fs(&mount, &count);
- return error;
- }
- static const char *policy_get_link(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
- {
- struct aa_ns *ns;
- struct path path;
- if (!dentry)
- return ERR_PTR(-ECHILD);
- ns = aa_get_current_ns();
- path.mnt = mntget(aafs_mnt);
- path.dentry = dget(ns_dir(ns));
- nd_jump_link(&path);
- aa_put_ns(ns);
- return NULL;
- }
- static int policy_readlink(struct dentry *dentry, char __user *buffer,
- int buflen)
- {
- char name[32];
- int res;
- res = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME,
- d_inode(dentry)->i_ino);
- if (res > 0 && res < sizeof(name))
- res = readlink_copy(buffer, buflen, name);
- else
- res = -ENOENT;
- return res;
- }
- static const struct inode_operations policy_link_iops = {
- .readlink = policy_readlink,
- .get_link = policy_get_link,
- };
- /**
- * aa_create_aafs - create the apparmor security filesystem
- *
- * dentries created here are released by aa_destroy_aafs
- *
- * Returns: error on failure
- */
- static int __init aa_create_aafs(void)
- {
- struct dentry *dent;
- int error;
- if (!apparmor_initialized)
- return 0;
- if (aa_sfs_entry.dentry) {
- AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
- return -EEXIST;
- }
- /* setup apparmorfs used to virtualize policy/ */
- aafs_mnt = kern_mount(&aafs_ops);
- if (IS_ERR(aafs_mnt))
- panic("can't set apparmorfs up\n");
- aafs_mnt->mnt_sb->s_flags &= ~SB_NOUSER;
- /* Populate fs tree. */
- error = entry_create_dir(&aa_sfs_entry, NULL);
- if (error)
- goto error;
- dent = securityfs_create_file(".load", 0666, aa_sfs_entry.dentry,
- NULL, &aa_fs_profile_load);
- if (IS_ERR(dent))
- goto dent_error;
- ns_subload(root_ns) = dent;
- dent = securityfs_create_file(".replace", 0666, aa_sfs_entry.dentry,
- NULL, &aa_fs_profile_replace);
- if (IS_ERR(dent))
- goto dent_error;
- ns_subreplace(root_ns) = dent;
- dent = securityfs_create_file(".remove", 0666, aa_sfs_entry.dentry,
- NULL, &aa_fs_profile_remove);
- if (IS_ERR(dent))
- goto dent_error;
- ns_subremove(root_ns) = dent;
- dent = securityfs_create_file("revision", 0444, aa_sfs_entry.dentry,
- NULL, &aa_fs_ns_revision_fops);
- if (IS_ERR(dent))
- goto dent_error;
- ns_subrevision(root_ns) = dent;
- /* policy tree referenced by magic policy symlink */
- mutex_lock_nested(&root_ns->lock, root_ns->level);
- error = __aafs_ns_mkdir(root_ns, aafs_mnt->mnt_root, ".policy",
- aafs_mnt->mnt_root);
- mutex_unlock(&root_ns->lock);
- if (error)
- goto error;
- /* magic symlink similar to nsfs redirects based on task policy */
- dent = securityfs_create_symlink("policy", aa_sfs_entry.dentry,
- NULL, &policy_link_iops);
- if (IS_ERR(dent))
- goto dent_error;
- error = aa_mk_null_file(aa_sfs_entry.dentry);
- if (error)
- goto error;
- /* TODO: add default profile to apparmorfs */
- /* Report that AppArmor fs is enabled */
- aa_info_message("AppArmor Filesystem Enabled");
- return 0;
- dent_error:
- error = PTR_ERR(dent);
- error:
- aa_destroy_aafs();
- AA_ERROR("Error creating AppArmor securityfs\n");
- return error;
- }
- fs_initcall(aa_create_aafs);
|