123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566 |
- /*
- * NSA Security-Enhanced Linux (SELinux) security module
- *
- * This file contains the SELinux hook function implementations.
- *
- * Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
- * Chris Vance, <cvance@nai.com>
- * Wayne Salamon, <wsalamon@nai.com>
- * James Morris <jmorris@redhat.com>
- *
- * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
- * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
- * <dgoeddel@trustedcs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- */
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/ptrace.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/security.h>
- #include <linux/xattr.h>
- #include <linux/capability.h>
- #include <linux/unistd.h>
- #include <linux/mm.h>
- #include <linux/mman.h>
- #include <linux/slab.h>
- #include <linux/pagemap.h>
- #include <linux/swap.h>
- #include <linux/smp_lock.h>
- #include <linux/spinlock.h>
- #include <linux/syscalls.h>
- #include <linux/file.h>
- #include <linux/namei.h>
- #include <linux/mount.h>
- #include <linux/ext2_fs.h>
- #include <linux/proc_fs.h>
- #include <linux/kd.h>
- #include <linux/netfilter_ipv4.h>
- #include <linux/netfilter_ipv6.h>
- #include <linux/tty.h>
- #include <net/icmp.h>
- #include <net/ip.h> /* for sysctl_local_port_range[] */
- #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
- #include <asm/uaccess.h>
- #include <asm/semaphore.h>
- #include <asm/ioctls.h>
- #include <linux/bitops.h>
- #include <linux/interrupt.h>
- #include <linux/netdevice.h> /* for network interface checks */
- #include <linux/netlink.h>
- #include <linux/tcp.h>
- #include <linux/udp.h>
- #include <linux/quota.h>
- #include <linux/un.h> /* for Unix socket types */
- #include <net/af_unix.h> /* for Unix socket types */
- #include <linux/parser.h>
- #include <linux/nfs_mount.h>
- #include <net/ipv6.h>
- #include <linux/hugetlb.h>
- #include <linux/personality.h>
- #include <linux/sysctl.h>
- #include <linux/audit.h>
- #include "avc.h"
- #include "objsec.h"
- #include "netif.h"
- #define XATTR_SELINUX_SUFFIX "selinux"
- #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
- extern unsigned int policydb_loaded_version;
- extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
- #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
- int selinux_enforcing = 0;
- static int __init enforcing_setup(char *str)
- {
- selinux_enforcing = simple_strtol(str,NULL,0);
- return 1;
- }
- __setup("enforcing=", enforcing_setup);
- #endif
- #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
- int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
- static int __init selinux_enabled_setup(char *str)
- {
- selinux_enabled = simple_strtol(str, NULL, 0);
- return 1;
- }
- __setup("selinux=", selinux_enabled_setup);
- #endif
- /* Original (dummy) security module. */
- static struct security_operations *original_ops = NULL;
- /* Minimal support for a secondary security module,
- just to allow the use of the dummy or capability modules.
- The owlsm module can alternatively be used as a secondary
- module as long as CONFIG_OWLSM_FD is not enabled. */
- static struct security_operations *secondary_ops = NULL;
- /* Lists of inode and superblock security structures initialized
- before the policy was loaded. */
- static LIST_HEAD(superblock_security_head);
- static DEFINE_SPINLOCK(sb_security_lock);
- /* Allocate and free functions for each kind of security blob. */
- static int task_alloc_security(struct task_struct *task)
- {
- struct task_security_struct *tsec;
- tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
- if (!tsec)
- return -ENOMEM;
- memset(tsec, 0, sizeof(struct task_security_struct));
- tsec->magic = SELINUX_MAGIC;
- tsec->task = task;
- tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
- task->security = tsec;
- return 0;
- }
- static void task_free_security(struct task_struct *task)
- {
- struct task_security_struct *tsec = task->security;
- if (!tsec || tsec->magic != SELINUX_MAGIC)
- return;
- task->security = NULL;
- kfree(tsec);
- }
- static int inode_alloc_security(struct inode *inode)
- {
- struct task_security_struct *tsec = current->security;
- struct inode_security_struct *isec;
- isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
- if (!isec)
- return -ENOMEM;
- memset(isec, 0, sizeof(struct inode_security_struct));
- init_MUTEX(&isec->sem);
- INIT_LIST_HEAD(&isec->list);
- isec->magic = SELINUX_MAGIC;
- isec->inode = inode;
- isec->sid = SECINITSID_UNLABELED;
- isec->sclass = SECCLASS_FILE;
- if (tsec && tsec->magic == SELINUX_MAGIC)
- isec->task_sid = tsec->sid;
- else
- isec->task_sid = SECINITSID_UNLABELED;
- inode->i_security = isec;
- return 0;
- }
- static void inode_free_security(struct inode *inode)
- {
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- if (!isec || isec->magic != SELINUX_MAGIC)
- return;
- spin_lock(&sbsec->isec_lock);
- if (!list_empty(&isec->list))
- list_del_init(&isec->list);
- spin_unlock(&sbsec->isec_lock);
- inode->i_security = NULL;
- kfree(isec);
- }
- static int file_alloc_security(struct file *file)
- {
- struct task_security_struct *tsec = current->security;
- struct file_security_struct *fsec;
- fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
- if (!fsec)
- return -ENOMEM;
- memset(fsec, 0, sizeof(struct file_security_struct));
- fsec->magic = SELINUX_MAGIC;
- fsec->file = file;
- if (tsec && tsec->magic == SELINUX_MAGIC) {
- fsec->sid = tsec->sid;
- fsec->fown_sid = tsec->sid;
- } else {
- fsec->sid = SECINITSID_UNLABELED;
- fsec->fown_sid = SECINITSID_UNLABELED;
- }
- file->f_security = fsec;
- return 0;
- }
- static void file_free_security(struct file *file)
- {
- struct file_security_struct *fsec = file->f_security;
- if (!fsec || fsec->magic != SELINUX_MAGIC)
- return;
- file->f_security = NULL;
- kfree(fsec);
- }
- static int superblock_alloc_security(struct super_block *sb)
- {
- struct superblock_security_struct *sbsec;
- sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
- if (!sbsec)
- return -ENOMEM;
- memset(sbsec, 0, sizeof(struct superblock_security_struct));
- init_MUTEX(&sbsec->sem);
- INIT_LIST_HEAD(&sbsec->list);
- INIT_LIST_HEAD(&sbsec->isec_head);
- spin_lock_init(&sbsec->isec_lock);
- sbsec->magic = SELINUX_MAGIC;
- sbsec->sb = sb;
- sbsec->sid = SECINITSID_UNLABELED;
- sbsec->def_sid = SECINITSID_FILE;
- sb->s_security = sbsec;
- return 0;
- }
- static void superblock_free_security(struct super_block *sb)
- {
- struct superblock_security_struct *sbsec = sb->s_security;
- if (!sbsec || sbsec->magic != SELINUX_MAGIC)
- return;
- spin_lock(&sb_security_lock);
- if (!list_empty(&sbsec->list))
- list_del_init(&sbsec->list);
- spin_unlock(&sb_security_lock);
- sb->s_security = NULL;
- kfree(sbsec);
- }
- #ifdef CONFIG_SECURITY_NETWORK
- static int sk_alloc_security(struct sock *sk, int family, int priority)
- {
- struct sk_security_struct *ssec;
- if (family != PF_UNIX)
- return 0;
- ssec = kmalloc(sizeof(*ssec), priority);
- if (!ssec)
- return -ENOMEM;
- memset(ssec, 0, sizeof(*ssec));
- ssec->magic = SELINUX_MAGIC;
- ssec->sk = sk;
- ssec->peer_sid = SECINITSID_UNLABELED;
- sk->sk_security = ssec;
- return 0;
- }
- static void sk_free_security(struct sock *sk)
- {
- struct sk_security_struct *ssec = sk->sk_security;
- if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
- return;
- sk->sk_security = NULL;
- kfree(ssec);
- }
- #endif /* CONFIG_SECURITY_NETWORK */
- /* The security server must be initialized before
- any labeling or access decisions can be provided. */
- extern int ss_initialized;
- /* The file system's label must be initialized prior to use. */
- static char *labeling_behaviors[6] = {
- "uses xattr",
- "uses transition SIDs",
- "uses task SIDs",
- "uses genfs_contexts",
- "not configured for labeling",
- "uses mountpoint labeling",
- };
- static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
- static inline int inode_doinit(struct inode *inode)
- {
- return inode_doinit_with_dentry(inode, NULL);
- }
- enum {
- Opt_context = 1,
- Opt_fscontext = 2,
- Opt_defcontext = 4,
- };
- static match_table_t tokens = {
- {Opt_context, "context=%s"},
- {Opt_fscontext, "fscontext=%s"},
- {Opt_defcontext, "defcontext=%s"},
- };
- #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
- static int try_context_mount(struct super_block *sb, void *data)
- {
- char *context = NULL, *defcontext = NULL;
- const char *name;
- u32 sid;
- int alloc = 0, rc = 0, seen = 0;
- struct task_security_struct *tsec = current->security;
- struct superblock_security_struct *sbsec = sb->s_security;
- if (!data)
- goto out;
- name = sb->s_type->name;
- if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
- /* NFS we understand. */
- if (!strcmp(name, "nfs")) {
- struct nfs_mount_data *d = data;
- if (d->version < NFS_MOUNT_VERSION)
- goto out;
- if (d->context[0]) {
- context = d->context;
- seen |= Opt_context;
- }
- } else
- goto out;
- } else {
- /* Standard string-based options. */
- char *p, *options = data;
- while ((p = strsep(&options, ",")) != NULL) {
- int token;
- substring_t args[MAX_OPT_ARGS];
- if (!*p)
- continue;
- token = match_token(p, tokens, args);
- switch (token) {
- case Opt_context:
- if (seen) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- context = match_strdup(&args[0]);
- if (!context) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_context;
- break;
- case Opt_fscontext:
- if (seen & (Opt_context|Opt_fscontext)) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- context = match_strdup(&args[0]);
- if (!context) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_fscontext;
- break;
- case Opt_defcontext:
- if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
- rc = -EINVAL;
- printk(KERN_WARNING "SELinux: "
- "defcontext option is invalid "
- "for this filesystem type\n");
- goto out_free;
- }
- if (seen & (Opt_context|Opt_defcontext)) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- defcontext = match_strdup(&args[0]);
- if (!defcontext) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_defcontext;
- break;
- default:
- rc = -EINVAL;
- printk(KERN_WARNING "SELinux: unknown mount "
- "option\n");
- goto out_free;
- }
- }
- }
- if (!seen)
- goto out;
- if (context) {
- rc = security_context_to_sid(context, strlen(context), &sid);
- if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- context, sb->s_id, name, rc);
- goto out_free;
- }
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL);
- if (rc)
- goto out_free;
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELTO, NULL);
- if (rc)
- goto out_free;
- sbsec->sid = sid;
- if (seen & Opt_context)
- sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
- }
- if (defcontext) {
- rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
- if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- defcontext, sb->s_id, name, rc);
- goto out_free;
- }
- if (sid == sbsec->def_sid)
- goto out_free;
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL);
- if (rc)
- goto out_free;
- rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, NULL);
- if (rc)
- goto out_free;
- sbsec->def_sid = sid;
- }
- out_free:
- if (alloc) {
- kfree(context);
- kfree(defcontext);
- }
- out:
- return rc;
- }
- static int superblock_doinit(struct super_block *sb, void *data)
- {
- struct superblock_security_struct *sbsec = sb->s_security;
- struct dentry *root = sb->s_root;
- struct inode *inode = root->d_inode;
- int rc = 0;
- down(&sbsec->sem);
- if (sbsec->initialized)
- goto out;
- if (!ss_initialized) {
- /* Defer initialization until selinux_complete_init,
- after the initial policy is loaded and the security
- server is ready to handle calls. */
- spin_lock(&sb_security_lock);
- if (list_empty(&sbsec->list))
- list_add(&sbsec->list, &superblock_security_head);
- spin_unlock(&sb_security_lock);
- goto out;
- }
- /* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
- if (rc) {
- printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
- __FUNCTION__, sb->s_type->name, rc);
- goto out;
- }
- rc = try_context_mount(sb, data);
- if (rc)
- goto out;
- if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
- /* Make sure that the xattr handler exists and that no
- error other than -ENODATA is returned by getxattr on
- the root directory. -ENODATA is ok, as this may be
- the first boot of the SELinux kernel before we have
- assigned xattr values to the filesystem. */
- if (!inode->i_op->getxattr) {
- printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
- "xattr support\n", sb->s_id, sb->s_type->name);
- rc = -EOPNOTSUPP;
- goto out;
- }
- rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
- if (rc < 0 && rc != -ENODATA) {
- if (rc == -EOPNOTSUPP)
- printk(KERN_WARNING "SELinux: (dev %s, type "
- "%s) has no security xattr handler\n",
- sb->s_id, sb->s_type->name);
- else
- printk(KERN_WARNING "SELinux: (dev %s, type "
- "%s) getxattr errno %d\n", sb->s_id,
- sb->s_type->name, -rc);
- goto out;
- }
- }
- if (strcmp(sb->s_type->name, "proc") == 0)
- sbsec->proc = 1;
- sbsec->initialized = 1;
- if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
- printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n",
- sb->s_id, sb->s_type->name);
- }
- else {
- printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n",
- sb->s_id, sb->s_type->name,
- labeling_behaviors[sbsec->behavior-1]);
- }
- /* Initialize the root inode. */
- rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
- /* Initialize any other inodes associated with the superblock, e.g.
- inodes created prior to initial policy load or inodes created
- during get_sb by a pseudo filesystem that directly
- populates itself. */
- spin_lock(&sbsec->isec_lock);
- next_inode:
- if (!list_empty(&sbsec->isec_head)) {
- struct inode_security_struct *isec =
- list_entry(sbsec->isec_head.next,
- struct inode_security_struct, list);
- struct inode *inode = isec->inode;
- spin_unlock(&sbsec->isec_lock);
- inode = igrab(inode);
- if (inode) {
- if (!IS_PRIVATE (inode))
- inode_doinit(inode);
- iput(inode);
- }
- spin_lock(&sbsec->isec_lock);
- list_del_init(&isec->list);
- goto next_inode;
- }
- spin_unlock(&sbsec->isec_lock);
- out:
- up(&sbsec->sem);
- return rc;
- }
- static inline u16 inode_mode_to_security_class(umode_t mode)
- {
- switch (mode & S_IFMT) {
- case S_IFSOCK:
- return SECCLASS_SOCK_FILE;
- case S_IFLNK:
- return SECCLASS_LNK_FILE;
- case S_IFREG:
- return SECCLASS_FILE;
- case S_IFBLK:
- return SECCLASS_BLK_FILE;
- case S_IFDIR:
- return SECCLASS_DIR;
- case S_IFCHR:
- return SECCLASS_CHR_FILE;
- case S_IFIFO:
- return SECCLASS_FIFO_FILE;
- }
- return SECCLASS_FILE;
- }
- static inline u16 socket_type_to_security_class(int family, int type, int protocol)
- {
- switch (family) {
- case PF_UNIX:
- switch (type) {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- return SECCLASS_UNIX_STREAM_SOCKET;
- case SOCK_DGRAM:
- return SECCLASS_UNIX_DGRAM_SOCKET;
- }
- break;
- case PF_INET:
- case PF_INET6:
- switch (type) {
- case SOCK_STREAM:
- return SECCLASS_TCP_SOCKET;
- case SOCK_DGRAM:
- return SECCLASS_UDP_SOCKET;
- case SOCK_RAW:
- return SECCLASS_RAWIP_SOCKET;
- }
- break;
- case PF_NETLINK:
- switch (protocol) {
- case NETLINK_ROUTE:
- return SECCLASS_NETLINK_ROUTE_SOCKET;
- case NETLINK_FIREWALL:
- return SECCLASS_NETLINK_FIREWALL_SOCKET;
- case NETLINK_TCPDIAG:
- return SECCLASS_NETLINK_TCPDIAG_SOCKET;
- case NETLINK_NFLOG:
- return SECCLASS_NETLINK_NFLOG_SOCKET;
- case NETLINK_XFRM:
- return SECCLASS_NETLINK_XFRM_SOCKET;
- case NETLINK_SELINUX:
- return SECCLASS_NETLINK_SELINUX_SOCKET;
- case NETLINK_AUDIT:
- return SECCLASS_NETLINK_AUDIT_SOCKET;
- case NETLINK_IP6_FW:
- return SECCLASS_NETLINK_IP6FW_SOCKET;
- case NETLINK_DNRTMSG:
- return SECCLASS_NETLINK_DNRT_SOCKET;
- case NETLINK_KOBJECT_UEVENT:
- return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET;
- default:
- return SECCLASS_NETLINK_SOCKET;
- }
- case PF_PACKET:
- return SECCLASS_PACKET_SOCKET;
- case PF_KEY:
- return SECCLASS_KEY_SOCKET;
- }
- return SECCLASS_SOCKET;
- }
- #ifdef CONFIG_PROC_FS
- static int selinux_proc_get_sid(struct proc_dir_entry *de,
- u16 tclass,
- u32 *sid)
- {
- int buflen, rc;
- char *buffer, *path, *end;
- buffer = (char*)__get_free_page(GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- buflen = PAGE_SIZE;
- end = buffer+buflen;
- *--end = '\0';
- buflen--;
- path = end-1;
- *path = '/';
- while (de && de != de->parent) {
- buflen -= de->namelen + 1;
- if (buflen < 0)
- break;
- end -= de->namelen;
- memcpy(end, de->name, de->namelen);
- *--end = '/';
- path = end;
- de = de->parent;
- }
- rc = security_genfs_sid("proc", path, tclass, sid);
- free_page((unsigned long)buffer);
- return rc;
- }
- #else
- static int selinux_proc_get_sid(struct proc_dir_entry *de,
- u16 tclass,
- u32 *sid)
- {
- return -EINVAL;
- }
- #endif
- /* The inode's security attributes must be initialized before first use. */
- static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
- {
- struct superblock_security_struct *sbsec = NULL;
- struct inode_security_struct *isec = inode->i_security;
- u32 sid;
- struct dentry *dentry;
- #define INITCONTEXTLEN 255
- char *context = NULL;
- unsigned len = 0;
- int rc = 0;
- int hold_sem = 0;
- if (isec->initialized)
- goto out;
- down(&isec->sem);
- hold_sem = 1;
- if (isec->initialized)
- goto out;
- sbsec = inode->i_sb->s_security;
- if (!sbsec->initialized) {
- /* Defer initialization until selinux_complete_init,
- after the initial policy is loaded and the security
- server is ready to handle calls. */
- spin_lock(&sbsec->isec_lock);
- if (list_empty(&isec->list))
- list_add(&isec->list, &sbsec->isec_head);
- spin_unlock(&sbsec->isec_lock);
- goto out;
- }
- switch (sbsec->behavior) {
- case SECURITY_FS_USE_XATTR:
- if (!inode->i_op->getxattr) {
- isec->sid = sbsec->def_sid;
- break;
- }
- /* Need a dentry, since the xattr API requires one.
- Life would be simpler if we could just pass the inode. */
- if (opt_dentry) {
- /* Called from d_instantiate or d_splice_alias. */
- dentry = dget(opt_dentry);
- } else {
- /* Called from selinux_complete_init, try to find a dentry. */
- dentry = d_find_alias(inode);
- }
- if (!dentry) {
- printk(KERN_WARNING "%s: no dentry for dev=%s "
- "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id,
- inode->i_ino);
- goto out;
- }
- len = INITCONTEXTLEN;
- context = kmalloc(len, GFP_KERNEL);
- if (!context) {
- rc = -ENOMEM;
- dput(dentry);
- goto out;
- }
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
- context, len);
- if (rc == -ERANGE) {
- /* Need a larger buffer. Query for the right size. */
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
- NULL, 0);
- if (rc < 0) {
- dput(dentry);
- goto out;
- }
- kfree(context);
- len = rc;
- context = kmalloc(len, GFP_KERNEL);
- if (!context) {
- rc = -ENOMEM;
- dput(dentry);
- goto out;
- }
- rc = inode->i_op->getxattr(dentry,
- XATTR_NAME_SELINUX,
- context, len);
- }
- dput(dentry);
- if (rc < 0) {
- if (rc != -ENODATA) {
- printk(KERN_WARNING "%s: getxattr returned "
- "%d for dev=%s ino=%ld\n", __FUNCTION__,
- -rc, inode->i_sb->s_id, inode->i_ino);
- kfree(context);
- goto out;
- }
- /* Map ENODATA to the default file SID */
- sid = sbsec->def_sid;
- rc = 0;
- } else {
- rc = security_context_to_sid(context, rc, &sid);
- if (rc) {
- printk(KERN_WARNING "%s: context_to_sid(%s) "
- "returned %d for dev=%s ino=%ld\n",
- __FUNCTION__, context, -rc,
- inode->i_sb->s_id, inode->i_ino);
- kfree(context);
- /* Leave with the unlabeled SID */
- rc = 0;
- break;
- }
- }
- kfree(context);
- isec->sid = sid;
- break;
- case SECURITY_FS_USE_TASK:
- isec->sid = isec->task_sid;
- break;
- case SECURITY_FS_USE_TRANS:
- /* Default to the fs SID. */
- isec->sid = sbsec->sid;
- /* Try to obtain a transition SID. */
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = security_transition_sid(isec->task_sid,
- sbsec->sid,
- isec->sclass,
- &sid);
- if (rc)
- goto out;
- isec->sid = sid;
- break;
- default:
- /* Default to the fs SID. */
- isec->sid = sbsec->sid;
- if (sbsec->proc) {
- struct proc_inode *proci = PROC_I(inode);
- if (proci->pde) {
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = selinux_proc_get_sid(proci->pde,
- isec->sclass,
- &sid);
- if (rc)
- goto out;
- isec->sid = sid;
- }
- }
- break;
- }
- isec->initialized = 1;
- out:
- if (isec->sclass == SECCLASS_FILE)
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- if (hold_sem)
- up(&isec->sem);
- return rc;
- }
- /* Convert a Linux signal to an access vector. */
- static inline u32 signal_to_av(int sig)
- {
- u32 perm = 0;
- switch (sig) {
- case SIGCHLD:
- /* Commonly granted from child to parent. */
- perm = PROCESS__SIGCHLD;
- break;
- case SIGKILL:
- /* Cannot be caught or ignored */
- perm = PROCESS__SIGKILL;
- break;
- case SIGSTOP:
- /* Cannot be caught or ignored */
- perm = PROCESS__SIGSTOP;
- break;
- default:
- /* All other signals. */
- perm = PROCESS__SIGNAL;
- break;
- }
- return perm;
- }
- /* Check permission betweeen a pair of tasks, e.g. signal checks,
- fork check, ptrace check, etc. */
- static int task_has_perm(struct task_struct *tsk1,
- struct task_struct *tsk2,
- u32 perms)
- {
- struct task_security_struct *tsec1, *tsec2;
- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
- return avc_has_perm(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, perms, NULL);
- }
- /* Check whether a task is allowed to use a capability. */
- static int task_has_capability(struct task_struct *tsk,
- int cap)
- {
- struct task_security_struct *tsec;
- struct avc_audit_data ad;
- tsec = tsk->security;
- AVC_AUDIT_DATA_INIT(&ad,CAP);
- ad.tsk = tsk;
- ad.u.cap = cap;
- return avc_has_perm(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
- }
- /* Check whether a task is allowed to use a system operation. */
- static int task_has_system(struct task_struct *tsk,
- u32 perms)
- {
- struct task_security_struct *tsec;
- tsec = tsk->security;
- return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
- SECCLASS_SYSTEM, perms, NULL);
- }
- /* Check whether a task has a particular permission to an inode.
- The 'adp' parameter is optional and allows other audit
- data to be passed (e.g. the dentry). */
- static int inode_has_perm(struct task_struct *tsk,
- struct inode *inode,
- u32 perms,
- struct avc_audit_data *adp)
- {
- struct task_security_struct *tsec;
- struct inode_security_struct *isec;
- struct avc_audit_data ad;
- tsec = tsk->security;
- isec = inode->i_security;
- if (!adp) {
- adp = &ad;
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.inode = inode;
- }
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
- }
- /* Same as inode_has_perm, but pass explicit audit data containing
- the dentry to help the auditing code to more easily generate the
- pathname if needed. */
- static inline int dentry_has_perm(struct task_struct *tsk,
- struct vfsmount *mnt,
- struct dentry *dentry,
- u32 av)
- {
- struct inode *inode = dentry->d_inode;
- struct avc_audit_data ad;
- AVC_AUDIT_DATA_INIT(&ad,FS);
- ad.u.fs.mnt = mnt;
- ad.u.fs.dentry = dentry;
- return inode_has_perm(tsk, inode, av, &ad);
- }
- /* Check whether a task can use an open file descriptor to
- access an inode in a given way. Check access to the
- descriptor itself, and then use dentry_has_perm to
- check a particular permission to the file.
- Access to the descriptor is implicitly granted if it
- has the same SID as the process. If av is zero, then
- access to the file is not checked, e.g. for cases
- where only the descriptor is affected like seek. */
- static inline int file_has_perm(struct task_struct *tsk,
- struct file *file,
- u32 av)
- {
- struct task_security_struct *tsec = tsk->security;
- struct file_security_struct *fsec = file->f_security;
- struct vfsmount *mnt = file->f_vfsmnt;
- struct dentry *dentry = file->f_dentry;
- struct inode *inode = dentry->d_inode;
- struct avc_audit_data ad;
- int rc;
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.mnt = mnt;
- ad.u.fs.dentry = dentry;
- if (tsec->sid != fsec->sid) {
- rc = avc_has_perm(tsec->sid, fsec->sid,
- SECCLASS_FD,
- FD__USE,
- &ad);
- if (rc)
- return rc;
- }
- /* av is zero if only checking access to the descriptor. */
- if (av)
- return inode_has_perm(tsk, inode, av, &ad);
- return 0;
- }
- /* Check whether a task can create a file. */
- static int may_create(struct inode *dir,
- struct dentry *dentry,
- u16 tclass)
- {
- struct task_security_struct *tsec;
- struct inode_security_struct *dsec;
- struct superblock_security_struct *sbsec;
- u32 newsid;
- struct avc_audit_data ad;
- int rc;
- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.dentry = dentry;
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
- DIR__ADD_NAME | DIR__SEARCH,
- &ad);
- if (rc)
- return rc;
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
- &newsid);
- if (rc)
- return rc;
- }
- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
- if (rc)
- return rc;
- return avc_has_perm(newsid, sbsec->sid,
- SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, &ad);
- }
- #define MAY_LINK 0
- #define MAY_UNLINK 1
- #define MAY_RMDIR 2
- /* Check whether a task can link, unlink, or rmdir a file/directory. */
- static int may_link(struct inode *dir,
- struct dentry *dentry,
- int kind)
- {
- struct task_security_struct *tsec;
- struct inode_security_struct *dsec, *isec;
- struct avc_audit_data ad;
- u32 av;
- int rc;
- tsec = current->security;
- dsec = dir->i_security;
- isec = dentry->d_inode->i_security;
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.dentry = dentry;
- av = DIR__SEARCH;
- av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
- if (rc)
- return rc;
- switch (kind) {
- case MAY_LINK:
- av = FILE__LINK;
- break;
- case MAY_UNLINK:
- av = FILE__UNLINK;
- break;
- case MAY_RMDIR:
- av = DIR__RMDIR;
- break;
- default:
- printk(KERN_WARNING "may_link: unrecognized kind %d\n", kind);
- return 0;
- }
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
- return rc;
- }
- static inline int may_rename(struct inode *old_dir,
- struct dentry *old_dentry,
- struct inode *new_dir,
- struct dentry *new_dentry)
- {
- struct task_security_struct *tsec;
- struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
- struct avc_audit_data ad;
- u32 av;
- int old_is_dir, new_is_dir;
- int rc;
- tsec = current->security;
- old_dsec = old_dir->i_security;
- old_isec = old_dentry->d_inode->i_security;
- old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
- new_dsec = new_dir->i_security;
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.dentry = old_dentry;
- rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
- DIR__REMOVE_NAME | DIR__SEARCH, &ad);
- if (rc)
- return rc;
- rc = avc_has_perm(tsec->sid, old_isec->sid,
- old_isec->sclass, FILE__RENAME, &ad);
- if (rc)
- return rc;
- if (old_is_dir && new_dir != old_dir) {
- rc = avc_has_perm(tsec->sid, old_isec->sid,
- old_isec->sclass, DIR__REPARENT, &ad);
- if (rc)
- return rc;
- }
- ad.u.fs.dentry = new_dentry;
- av = DIR__ADD_NAME | DIR__SEARCH;
- if (new_dentry->d_inode)
- av |= DIR__REMOVE_NAME;
- rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
- if (rc)
- return rc;
- if (new_dentry->d_inode) {
- new_isec = new_dentry->d_inode->i_security;
- new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
- rc = avc_has_perm(tsec->sid, new_isec->sid,
- new_isec->sclass,
- (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
- if (rc)
- return rc;
- }
- return 0;
- }
- /* Check whether a task can perform a filesystem operation. */
- static int superblock_has_perm(struct task_struct *tsk,
- struct super_block *sb,
- u32 perms,
- struct avc_audit_data *ad)
- {
- struct task_security_struct *tsec;
- struct superblock_security_struct *sbsec;
- tsec = tsk->security;
- sbsec = sb->s_security;
- return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- perms, ad);
- }
- /* Convert a Linux mode and permission mask to an access vector. */
- static inline u32 file_mask_to_av(int mode, int mask)
- {
- u32 av = 0;
- if ((mode & S_IFMT) != S_IFDIR) {
- if (mask & MAY_EXEC)
- av |= FILE__EXECUTE;
- if (mask & MAY_READ)
- av |= FILE__READ;
- if (mask & MAY_APPEND)
- av |= FILE__APPEND;
- else if (mask & MAY_WRITE)
- av |= FILE__WRITE;
- } else {
- if (mask & MAY_EXEC)
- av |= DIR__SEARCH;
- if (mask & MAY_WRITE)
- av |= DIR__WRITE;
- if (mask & MAY_READ)
- av |= DIR__READ;
- }
- return av;
- }
- /* Convert a Linux file to an access vector. */
- static inline u32 file_to_av(struct file *file)
- {
- u32 av = 0;
- if (file->f_mode & FMODE_READ)
- av |= FILE__READ;
- if (file->f_mode & FMODE_WRITE) {
- if (file->f_flags & O_APPEND)
- av |= FILE__APPEND;
- else
- av |= FILE__WRITE;
- }
- return av;
- }
- /* Set an inode's SID to a specified value. */
- static int inode_security_set_sid(struct inode *inode, u32 sid)
- {
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- if (!sbsec->initialized) {
- /* Defer initialization to selinux_complete_init. */
- return 0;
- }
- down(&isec->sem);
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- isec->sid = sid;
- isec->initialized = 1;
- up(&isec->sem);
- return 0;
- }
- /* Set the security attributes on a newly created file. */
- static int post_create(struct inode *dir,
- struct dentry *dentry)
- {
- struct task_security_struct *tsec;
- struct inode *inode;
- struct inode_security_struct *dsec;
- struct superblock_security_struct *sbsec;
- u32 newsid;
- char *context;
- unsigned int len;
- int rc;
- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
- inode = dentry->d_inode;
- if (!inode) {
- /* Some file system types (e.g. NFS) may not instantiate
- a dentry for all create operations (e.g. symlink),
- so we have to check to see if the inode is non-NULL. */
- printk(KERN_WARNING "post_create: no inode, dir (dev=%s, "
- "ino=%ld)\n", dir->i_sb->s_id, dir->i_ino);
- return 0;
- }
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid,
- inode_mode_to_security_class(inode->i_mode),
- &newsid);
- if (rc) {
- printk(KERN_WARNING "post_create: "
- "security_transition_sid failed, rc=%d (dev=%s "
- "ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- }
- rc = inode_security_set_sid(inode, newsid);
- if (rc) {
- printk(KERN_WARNING "post_create: inode_security_set_sid "
- "failed, rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- if (sbsec->behavior == SECURITY_FS_USE_XATTR &&
- inode->i_op->setxattr) {
- /* Use extended attributes. */
- rc = security_sid_to_context(newsid, &context, &len);
- if (rc) {
- printk(KERN_WARNING "post_create: sid_to_context "
- "failed, rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- down(&inode->i_sem);
- rc = inode->i_op->setxattr(dentry,
- XATTR_NAME_SELINUX,
- context, len, 0);
- up(&inode->i_sem);
- kfree(context);
- if (rc < 0) {
- printk(KERN_WARNING "post_create: setxattr failed, "
- "rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- }
- return 0;
- }
- /* Hook functions begin here. */
- static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
- {
- struct task_security_struct *psec = parent->security;
- struct task_security_struct *csec = child->security;
- int rc;
- rc = secondary_ops->ptrace(parent,child);
- if (rc)
- return rc;
- rc = task_has_perm(parent, child, PROCESS__PTRACE);
- /* Save the SID of the tracing process for later use in apply_creds. */
- if (!rc)
- csec->ptrace_sid = psec->sid;
- return rc;
- }
- static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
- {
- int error;
- error = task_has_perm(current, target, PROCESS__GETCAP);
- if (error)
- return error;
- return secondary_ops->capget(target, effective, inheritable, permitted);
- }
- static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
- {
- int error;
- error = secondary_ops->capset_check(target, effective, inheritable, permitted);
- if (error)
- return error;
- return task_has_perm(current, target, PROCESS__SETCAP);
- }
- static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
- {
- secondary_ops->capset_set(target, effective, inheritable, permitted);
- }
- static int selinux_capable(struct task_struct *tsk, int cap)
- {
- int rc;
- rc = secondary_ops->capable(tsk, cap);
- if (rc)
- return rc;
- return task_has_capability(tsk,cap);
- }
- static int selinux_sysctl(ctl_table *table, int op)
- {
- int error = 0;
- u32 av;
- struct task_security_struct *tsec;
- u32 tsid;
- int rc;
- rc = secondary_ops->sysctl(table, op);
- if (rc)
- return rc;
- tsec = current->security;
- rc = selinux_proc_get_sid(table->de, (op == 001) ?
- SECCLASS_DIR : SECCLASS_FILE, &tsid);
- if (rc) {
- /* Default to the well-defined sysctl SID. */
- tsid = SECINITSID_SYSCTL;
- }
- /* The op values are "defined" in sysctl.c, thereby creating
- * a bad coupling between this module and sysctl.c */
- if(op == 001) {
- error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_DIR, DIR__SEARCH, NULL);
- } else {
- av = 0;
- if (op & 004)
- av |= FILE__READ;
- if (op & 002)
- av |= FILE__WRITE;
- if (av)
- error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_FILE, av, NULL);
- }
- return error;
- }
- static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
- {
- int rc = 0;
- if (!sb)
- return 0;
- switch (cmds) {
- case Q_SYNC:
- case Q_QUOTAON:
- case Q_QUOTAOFF:
- case Q_SETINFO:
- case Q_SETQUOTA:
- rc = superblock_has_perm(current,
- sb,
- FILESYSTEM__QUOTAMOD, NULL);
- break;
- case Q_GETFMT:
- case Q_GETINFO:
- case Q_GETQUOTA:
- rc = superblock_has_perm(current,
- sb,
- FILESYSTEM__QUOTAGET, NULL);
- break;
- default:
- rc = 0; /* let the kernel handle invalid cmds */
- break;
- }
- return rc;
- }
- static int selinux_quota_on(struct dentry *dentry)
- {
- return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
- }
- static int selinux_syslog(int type)
- {
- int rc;
- rc = secondary_ops->syslog(type);
- if (rc)
- return rc;
- switch (type) {
- case 3: /* Read last kernel messages */
- case 10: /* Return size of the log buffer */
- rc = task_has_system(current, SYSTEM__SYSLOG_READ);
- break;
- case 6: /* Disable logging to console */
- case 7: /* Enable logging to console */
- case 8: /* Set level of messages printed to console */
- rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
- break;
- case 0: /* Close log */
- case 1: /* Open log */
- case 2: /* Read from log */
- case 4: /* Read/clear last kernel messages */
- case 5: /* Clear ring buffer */
- default:
- rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
- break;
- }
- return rc;
- }
- /*
- * Check that a process has enough memory to allocate a new virtual
- * mapping. 0 means there is enough memory for the allocation to
- * succeed and -ENOMEM implies there is not.
- *
- * Note that secondary_ops->capable and task_has_perm_noaudit return 0
- * if the capability is granted, but __vm_enough_memory requires 1 if
- * the capability is granted.
- *
- * Do not audit the selinux permission check, as this is applied to all
- * processes that allocate mappings.
- */
- static int selinux_vm_enough_memory(long pages)
- {
- int rc, cap_sys_admin = 0;
- struct task_security_struct *tsec = current->security;
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (rc == 0)
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL);
- if (rc == 0)
- cap_sys_admin = 1;
- return __vm_enough_memory(pages, cap_sys_admin);
- }
- /* binprm security operations */
- static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
- {
- struct bprm_security_struct *bsec;
- bsec = kmalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
- if (!bsec)
- return -ENOMEM;
- memset(bsec, 0, sizeof *bsec);
- bsec->magic = SELINUX_MAGIC;
- bsec->bprm = bprm;
- bsec->sid = SECINITSID_UNLABELED;
- bsec->set = 0;
- bprm->security = bsec;
- return 0;
- }
- static int selinux_bprm_set_security(struct linux_binprm *bprm)
- {
- struct task_security_struct *tsec;
- struct inode *inode = bprm->file->f_dentry->d_inode;
- struct inode_security_struct *isec;
- struct bprm_security_struct *bsec;
- u32 newsid;
- struct avc_audit_data ad;
- int rc;
- rc = secondary_ops->bprm_set_security(bprm);
- if (rc)
- return rc;
- bsec = bprm->security;
- if (bsec->set)
- return 0;
- tsec = current->security;
- isec = inode->i_security;
- /* Default to the current task SID. */
- bsec->sid = tsec->sid;
- /* Reset create SID on execve. */
- tsec->create_sid = 0;
- if (tsec->exec_sid) {
- newsid = tsec->exec_sid;
- /* Reset exec SID on execve. */
- tsec->exec_sid = 0;
- } else {
- /* Check for a default transition on this program. */
- rc = security_transition_sid(tsec->sid, isec->sid,
- SECCLASS_PROCESS, &newsid);
- if (rc)
- return rc;
- }
- AVC_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.mnt = bprm->file->f_vfsmnt;
- ad.u.fs.dentry = bprm->file->f_dentry;
- if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
- newsid = tsec->sid;
- if (tsec->sid == newsid) {
- rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
- if (rc)
- return rc;
- } else {
- /* Check permissions for the transition. */
- rc = avc_has_perm(tsec->sid, newsid,
- SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
- if (rc)
- return rc;
- rc = avc_has_perm(newsid, isec->sid,
- SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
- if (rc)
- return rc;
- /* Clear any possibly unsafe personality bits on exec: */
- current->personality &= ~PER_CLEAR_ON_SETID;
- /* Set the security field to the new SID. */
- bsec->sid = newsid;
- }
- bsec->set = 1;
- return 0;
- }
- static int selinux_bprm_check_security (struct linux_binprm *bprm)
- {
- return secondary_ops->bprm_check_security(bprm);
- }
- static int selinux_bprm_secureexec (struct linux_binprm *bprm)
- {
- struct task_security_struct *tsec = current->security;
- int atsecure = 0;
- if (tsec->osid != tsec->sid) {
- /* Enable secure mode for SIDs transitions unless
- the noatsecure permission is granted between
- the two SIDs, i.e. ahp returns 0. */
- atsecure = avc_has_perm(tsec->osid, tsec->sid,
- SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL);
- }
- return (atsecure || secondary_ops->bprm_secureexec(bprm));
- }
- static void selinux_bprm_free_security(struct linux_binprm *bprm)
- {
- struct bprm_security_struct *bsec = bprm->security;
- bprm->security = NULL;
- kfree(bsec);
- }
- extern struct vfsmount *selinuxfs_mount;
- extern struct dentry *selinux_null;
- /* Derived from fs/exec.c:flush_old_files. */
- static inline void flush_unauthorized_files(struct files_struct * files)
- {
- struct avc_audit_data ad;
- struct file *file, *devnull = NULL;
- struct tty_struct *tty = current->signal->tty;
- long j = -1;
- if (tty) {
- file_list_lock();
- file = list_entry(tty->tty_files.next, typeof(*file), f_list);
- if (file) {
- /* Revalidate access to controlling tty.
- Use inode_has_perm on the tty inode directly rather
- than using file_has_perm, as this particular open
- file may belong to another process and we are only
- interested in the inode-based check here. */
- struct inode *inode = file->f_dentry->d_inode;
- if (inode_has_perm(current, inode,
- FILE__READ | FILE__WRITE, NULL)) {
- /* Reset controlling tty. */
- current->signal->tty = NULL;
- current->signal->tty_old_pgrp = 0;
- }
- }
- file_list_unlock();
- }
- /* Revalidate access to inherited open files. */
- AVC_AUDIT_DATA_INIT(&ad,FS);
- spin_lock(&files->file_lock);
- for (;;) {
- unsigned long set, i;
- int fd;
- j++;
- i = j * __NFDBITS;
- if (i >= files->max_fds || i >= files->max_fdset)
- break;
- set = files->open_fds->fds_bits[j];
- if (!set)
- continue;
- spin_unlock(&files->file_lock);
- for ( ; set ; i++,set >>= 1) {
- if (set & 1) {
- file = fget(i);
- if (!file)
- continue;
- if (file_has_perm(current,
- file,
- file_to_av(file))) {
- sys_close(i);
- fd = get_unused_fd();
- if (fd != i) {
- if (fd >= 0)
- put_unused_fd(fd);
- fput(file);
- continue;
- }
- if (devnull) {
- atomic_inc(&devnull->f_count);
- } else {
- devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
- if (!devnull) {
- put_unused_fd(fd);
- fput(file);
- continue;
- }
- }
- fd_install(fd, devnull);
- }
- fput(file);
- }
- }
- spin_lock(&files->file_lock);
- }
- spin_unlock(&files->file_lock);
- }
- static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
- {
- struct task_security_struct *tsec;
- struct bprm_security_struct *bsec;
- u32 sid;
- int rc;
- secondary_ops->bprm_apply_creds(bprm, unsafe);
- tsec = current->security;
- bsec = bprm->security;
- sid = bsec->sid;
- tsec->osid = tsec->sid;
- bsec->unsafe = 0;
- if (tsec->sid != sid) {
- /* Check for shared state. If not ok, leave SID
- unchanged and kill. */
- if (unsafe & LSM_UNSAFE_SHARE) {
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
- /* Check for ptracing, and update the task SID if ok.
- Otherwise, leave SID unchanged and kill. */
- if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- rc = avc_has_perm(tsec->ptrace_sid, sid,
- SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
- tsec->sid = sid;
- }
- }
- /*
- * called after apply_creds without the task lock held
- */
- static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
- {
- struct task_security_struct *tsec;
- struct rlimit *rlim, *initrlim;
- struct itimerval itimer;
- struct bprm_security_struct *bsec;
- int rc, i;
- tsec = current->security;
- bsec = bprm->security;
- if (bsec->unsafe) {
- force_sig_specific(SIGKILL, current);
- return;
- }
- if (tsec->osid == tsec->sid)
- return;
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(current->files);
- /* Check whether the new SID can inherit signal state
- from the old SID. If not, clear itimers to avoid
- subsequent signal generation and flush and unblock
- signals. This must occur _after_ the task SID has
- been updated so that any kill done after the flush
- will be checked against the new SID. */
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__SIGINH, NULL);
- if (rc) {
- memset(&itimer, 0, sizeof itimer);
- for (i = 0; i < 3; i++)
- do_setitimer(i, &itimer, NULL);
- flush_signals(current);
- spin_lock_irq(¤t->sighand->siglock);
- flush_signal_handlers(current, 1);
- sigemptyset(¤t->blocked);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
- /* Check whether the new SID can inherit resource limits
- from the old SID. If not, reset all soft limits to
- the lower of the current task's hard limit and the init
- task's soft limit. Note that the setting of hard limits
- (even to lower them) can be controlled by the setrlimit
- check. The inclusion of the init task's soft limit into
- the computation is to avoid resetting soft limits higher
- than the default soft limit for cases where the default
- is lower than the hard limit, e.g. RLIMIT_CORE or
- RLIMIT_STACK.*/
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__RLIMITINH, NULL);
- if (rc) {
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim = current->signal->rlim + i;
- initrlim = init_task.signal->rlim+i;
- rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur);
- }
- if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
- /*
- * This will cause RLIMIT_CPU calculations
- * to be refigured.
- */
- current->it_prof_expires = jiffies_to_cputime(1);
- }
- }
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
- wake_up_interruptible(¤t->parent->signal->wait_chldexit);
- }
- /* superblock security operations */
- static int selinux_sb_alloc_security(struct super_block *sb)
- {
- return superblock_alloc_security(sb);
- }
- static void selinux_sb_free_security(struct super_block *sb)
- {
- superblock_free_security(sb);
- }
- static inline int match_prefix(char *prefix, int plen, char *option, int olen)
- {
- if (plen > olen)
- return 0;
- return !memcmp(prefix, option, plen);
- }
- static inline int selinux_option(char *option, int len)
- {
- return (match_prefix("context=", sizeof("context=")-1, option, len) ||
- match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) ||
- match_prefix("defcontext=", sizeof("defcontext=")-1, option, len));
- }
- static inline void take_option(char **to, char *from, int *first, int len)
- {
- if (!*first) {
- **to = ',';
- *to += 1;
- }
- else
- *first = 0;
- memcpy(*to, from, len);
- *to += len;
- }
- static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy)
- {
- int fnosec, fsec, rc = 0;
- char *in_save, *in_curr, *in_end;
- char *sec_curr, *nosec_save, *nosec;
- in_curr = orig;
- sec_curr = copy;
- /* Binary mount data: just copy */
- if (type->fs_flags & FS_BINARY_MOUNTDATA) {
- copy_page(sec_curr, in_curr);
- goto out;
- }
- nosec = (char *)get_zeroed_page(GFP_KERNEL);
- if (!nosec) {
- rc = -ENOMEM;
- goto out;
- }
- nosec_save = nosec;
- fnosec = fsec = 1;
- in_save = in_end = orig;
- do {
- if (*in_end == ',' || *in_end == '\0') {
- int len = in_end - in_curr;
- if (selinux_option(in_curr, len))
- take_option(&sec_curr, in_curr, &fsec, len);
- else
- take_option(&nosec, in_curr, &fnosec, len);
- in_curr = in_end + 1;
- }
- } while (*in_end++);
- copy_page(in_save, nosec_save);
- out:
- return rc;
- }
- static int selinux_sb_kern_mount(struct super_block *sb, void *data)
- {
- struct avc_audit_data ad;
- int rc;
- rc = superblock_doinit(sb, data);
- if (rc)
- return rc;
- AVC_AUDIT_DATA_INIT(&ad,FS);
- ad.u.fs.dentry = sb->s_root;
- return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
- }
- static int selinux_sb_statfs(struct super_block *sb)
- {
- struct avc_audit_data ad;
- AVC_AUDIT_DATA_INIT(&ad,FS);
- ad.u.fs.dentry = sb->s_root;
- return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad);
- }
- static int selinux_mount(char * dev_name,
- struct nameidata *nd,
- char * type,
- unsigned long flags,
- void * data)
- {
- int rc;
- rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
- if (rc)
- return rc;
- if (flags & MS_REMOUNT)
- return superblock_has_perm(current, nd->mnt->mnt_sb,
- FILESYSTEM__REMOUNT, NULL);
- else
- return dentry_has_perm(current, nd->mnt, nd->dentry,
- FILE__MOUNTON);
- }
- static int selinux_umount(struct vfsmount *mnt, int flags)
- {
- int rc;
- rc = secondary_ops->sb_umount(mnt, flags);
- if (rc)
- return rc;
- return superblock_has_perm(current,mnt->mnt_sb,
- FILESYSTEM__UNMOUNT,NULL);
- }
- /* inode security operations */
- static int selinux_inode_alloc_security(struct inode *inode)
- {
- return inode_alloc_security(inode);
- }
- static void selinux_inode_free_security(struct inode *inode)
- {
- inode_free_security(inode);
- }
- static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
- {
- return may_create(dir, dentry, SECCLASS_FILE);
- }
- static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask)
- {
- post_create(dir, dentry);
- }
- static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
- {
- int rc;
- rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
- if (rc)
- return rc;
- return may_link(dir, old_dentry, MAY_LINK);
- }
- static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry)
- {
- return;
- }
- static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
- {
- int rc;
- rc = secondary_ops->inode_unlink(dir, dentry);
- if (rc)
- return rc;
- return may_link(dir, dentry, MAY_UNLINK);
- }
- static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
- {
- return may_create(dir, dentry, SECCLASS_LNK_FILE);
- }
- static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name)
- {
- post_create(dir, dentry);
- }
- static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
- {
- return may_create(dir, dentry, SECCLASS_DIR);
- }
- static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask)
- {
- post_create(dir, dentry);
- }
- static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
- {
- return may_link(dir, dentry, MAY_RMDIR);
- }
- static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
- {
- int rc;
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
- if (rc)
- return rc;
- return may_create(dir, dentry, inode_mode_to_security_class(mode));
- }
- static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
- {
- post_create(dir, dentry);
- }
- static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
- {
- return may_rename(old_inode, old_dentry, new_inode, new_dentry);
- }
- static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
- {
- return;
- }
- static int selinux_inode_readlink(struct dentry *dentry)
- {
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
- }
- static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
- {
- int rc;
- rc = secondary_ops->inode_follow_link(dentry,nameidata);
- if (rc)
- return rc;
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
- }
- static int selinux_inode_permission(struct inode *inode, int mask,
- struct nameidata *nd)
- {
- int rc;
- rc = secondary_ops->inode_permission(inode, mask, nd);
- if (rc)
- return rc;
- if (!mask) {
- /* No permission to check. Existence test. */
- return 0;
- }
- return inode_has_perm(current, inode,
- file_mask_to_av(inode->i_mode, mask), NULL);
- }
- static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
- {
- int rc;
- rc = secondary_ops->inode_setattr(dentry, iattr);
- if (rc)
- return rc;
- if (iattr->ia_valid & ATTR_FORCE)
- return 0;
- if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
- ATTR_ATIME_SET | ATTR_MTIME_SET))
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
- return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
- }
- static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
- {
- return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
- }
- static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
- {
- struct task_security_struct *tsec = current->security;
- struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec;
- struct avc_audit_data ad;
- u32 newsid;
- int rc = 0;
- if (strcmp(name, XATTR_NAME_SELINUX)) {
- if (!strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof XATTR_SECURITY_PREFIX - 1) &&
- !capable(CAP_SYS_ADMIN)) {
- /* A different attribute in the security namespace.
- Restrict to administrator. */
- return -EPERM;
- }
- /* Not an attribute we recognize, so just check the
- ordinary setattr permission. */
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
- }
- sbsec = inode->i_sb->s_security;
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
- return -EOPNOTSUPP;
- if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
- return -EPERM;
- AVC_AUDIT_DATA_INIT(&ad,FS);
- ad.u.fs.dentry = dentry;
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- FILE__RELABELFROM, &ad);
- if (rc)
- return rc;
- rc = security_context_to_sid(value, size, &newsid);
- if (rc)
- return rc;
- rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
- FILE__RELABELTO, &ad);
- if (rc)
- return rc;
- rc = security_validate_transition(isec->sid, newsid, tsec->sid,
- isec->sclass);
- if (rc)
- return rc;
- return avc_has_perm(newsid,
- sbsec->sid,
- SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE,
- &ad);
- }
- static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
- void *value, size_t size, int flags)
- {
- struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
- u32 newsid;
- int rc;
- if (strcmp(name, XATTR_NAME_SELINUX)) {
- /* Not an attribute we recognize, so nothing to do. */
- return;
- }
- rc = security_context_to_sid(value, size, &newsid);
- if (rc) {
- printk(KERN_WARNING "%s: unable to obtain SID for context "
- "%s, rc=%d\n", __FUNCTION__, (char*)value, -rc);
- return;
- }
- isec->sid = newsid;
- return;
- }
- static int selinux_inode_getxattr (struct dentry *dentry, char *name)
- {
- struct inode *inode = dentry->d_inode;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
- return -EOPNOTSUPP;
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
- }
- static int selinux_inode_listxattr (struct dentry *dentry)
- {
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
- }
- static int selinux_inode_removexattr (struct dentry *dentry, char *name)
- {
- if (strcmp(name, XATTR_NAME_SELINUX)) {
- if (!strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof XATTR_SECURITY_PREFIX - 1) &&
- !capable(CAP_SYS_ADMIN)) {
- /* A different attribute in the security namespace.
- Restrict to administrator. */
- return -EPERM;
- }
- /* Not an attribute we recognize, so just check the
- ordinary setattr permission. Might want a separate
- permission for removexattr. */
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
- }
- /* No one is allowed to remove a SELinux security label.
- You can change the label, but all data must be labeled. */
- return -EACCES;
- }
- static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
- {
- struct inode_security_struct *isec = inode->i_security;
- char *context;
- unsigned len;
- int rc;
- /* Permission check handled by selinux_inode_getxattr hook.*/
- if (strcmp(name, XATTR_SELINUX_SUFFIX))
- return -EOPNOTSUPP;
- rc = security_sid_to_context(isec->sid, &context, &len);
- if (rc)
- return rc;
- if (!buffer || !size) {
- kfree(context);
- return len;
- }
- if (size < len) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(buffer, context, len);
- kfree(context);
- return len;
- }
- static int selinux_inode_setsecurity(struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
- {
- struct inode_security_struct *isec = inode->i_security;
- u32 newsid;
- int rc;
- if (strcmp(name, XATTR_SELINUX_SUFFIX))
- return -EOPNOTSUPP;
- if (!value || !size)
- return -EACCES;
- rc = security_context_to_sid((void*)value, size, &newsid);
- if (rc)
- return rc;
- isec->sid = newsid;
- return 0;
- }
- static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
- {
- const int len = sizeof(XATTR_NAME_SELINUX);
- if (buffer && len <= buffer_size)
- memcpy(buffer, XATTR_NAME_SELINUX, len);
- return len;
- }
- /* file security operations */
- static int selinux_file_permission(struct file *file, int mask)
- {
- struct inode *inode = file->f_dentry->d_inode;
- if (!mask) {
- /* No permission to check. Existence test. */
- return 0;
- }
- /* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
- if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
- mask |= MAY_APPEND;
- return file_has_perm(current, file,
- file_mask_to_av(inode->i_mode, mask));
- }
- static int selinux_file_alloc_security(struct file *file)
- {
- return file_alloc_security(file);
- }
- static void selinux_file_free_security(struct file *file)
- {
- file_free_security(file);
- }
- static int selinux_file_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- int error = 0;
- switch (cmd) {
- case FIONREAD:
- /* fall through */
- case FIBMAP:
- /* fall through */
- case FIGETBSZ:
- /* fall through */
- case EXT2_IOC_GETFLAGS:
- /* fall through */
- case EXT2_IOC_GETVERSION:
- error = file_has_perm(current, file, FILE__GETATTR);
- break;
- case EXT2_IOC_SETFLAGS:
- /* fall through */
- case EXT2_IOC_SETVERSION:
- error = file_has_perm(current, file, FILE__SETATTR);
- break;
- /* sys_ioctl() checks */
- case FIONBIO:
- /* fall through */
- case FIOASYNC:
- error = file_has_perm(current, file, 0);
- break;
- case KDSKBENT:
- case KDSKBSENT:
- error = task_has_capability(current,CAP_SYS_TTY_CONFIG);
- break;
- /* default case assumes that the command will go
- * to the file's ioctl() function.
- */
- default:
- error = file_has_perm(current, file, FILE__IOCTL);
- }
- return error;
- }
- static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
- {
- #ifndef CONFIG_PPC32
- if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
- /*
- * We are making executable an anonymous mapping or a
- * private file mapping that will also be writable.
- * This has an additional check.
- */
- int rc = task_has_perm(current, current, PROCESS__EXECMEM);
- if (rc)
- return rc;
- }
- #endif
- if (file) {
- /* read access is always possible with a mapping */
- u32 av = FILE__READ;
- /* write access only matters if the mapping is shared */
- if (shared && (prot & PROT_WRITE))
- av |= FILE__WRITE;
- if (prot & PROT_EXEC)
- av |= FILE__EXECUTE;
- return file_has_perm(current, file, av);
- }
- return 0;
- }
- static int selinux_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
- {
- int rc;
- rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
- if (rc)
- return rc;
- if (selinux_checkreqprot)
- prot = reqprot;
- return file_map_prot_check(file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
- }
- static int selinux_file_mprotect(struct vm_area_struct *vma,
- unsigned long reqprot,
- unsigned long prot)
- {
- int rc;
- rc = secondary_ops->file_mprotect(vma, reqprot, prot);
- if (rc)
- return rc;
- if (selinux_checkreqprot)
- prot = reqprot;
- #ifndef CONFIG_PPC32
- if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) {
- /*
- * We are making executable a file mapping that has
- * had some COW done. Since pages might have been written,
- * check ability to execute the possibly modified content.
- * This typically should only occur for text relocations.
- */
- int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
- if (rc)
- return rc;
- }
- #endif
- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
- }
- static int selinux_file_lock(struct file *file, unsigned int cmd)
- {
- return file_has_perm(current, file, FILE__LOCK);
- }
- static int selinux_file_fcntl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- int err = 0;
- switch (cmd) {
- case F_SETFL:
- if (!file->f_dentry || !file->f_dentry->d_inode) {
- err = -EINVAL;
- break;
- }
- if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
- err = file_has_perm(current, file,FILE__WRITE);
- break;
- }
- /* fall through */
- case F_SETOWN:
- case F_SETSIG:
- case F_GETFL:
- case F_GETOWN:
- case F_GETSIG:
- /* Just check FD__USE permission */
- err = file_has_perm(current, file, 0);
- break;
- case F_GETLK:
- case F_SETLK:
- case F_SETLKW:
- #if BITS_PER_LONG == 32
- case F_GETLK64:
- case F_SETLK64:
- case F_SETLKW64:
- #endif
- if (!file->f_dentry || !file->f_dentry->d_inode) {
- err = -EINVAL;
- break;
- }
- err = file_has_perm(current, file, FILE__LOCK);
- break;
- }
- return err;
- }
- static int selinux_file_set_fowner(struct file *file)
- {
- struct task_security_struct *tsec;
- struct file_security_struct *fsec;
- tsec = current->security;
- fsec = file->f_security;
- fsec->fown_sid = tsec->sid;
- return 0;
- }
- static int selinux_file_send_sigiotask(struct task_struct *tsk,
- struct fown_struct *fown, int signum)
- {
- struct file *file;
- u32 perm;
- struct task_security_struct *tsec;
- struct file_security_struct *fsec;
- /* struct fown_struct is never outside the context of a struct file */
- file = (struct file *)((long)fown - offsetof(struct file,f_owner));
- tsec = tsk->security;
- fsec = file->f_security;
- if (!signum)
- perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
- else
- perm = signal_to_av(signum);
- return avc_has_perm(fsec->fown_sid, tsec->sid,
- SECCLASS_PROCESS, perm, NULL);
- }
- static int selinux_file_receive(struct file *file)
- {
- return file_has_perm(current, file, file_to_av(file));
- }
- /* task security operations */
- static int selinux_task_create(unsigned long clone_flags)
- {
- int rc;
- rc = secondary_ops->task_create(clone_flags);
- if (rc)
- return rc;
- return task_has_perm(current, current, PROCESS__FORK);
- }
- static int selinux_task_alloc_security(struct task_struct *tsk)
- {
- struct task_security_struct *tsec1, *tsec2;
- int rc;
- tsec1 = current->security;
- rc = task_alloc_security(tsk);
- if (rc)
- return rc;
- tsec2 = tsk->security;
- tsec2->osid = tsec1->osid;
- tsec2->sid = tsec1->sid;
- /* Retain the exec and create SIDs across fork */
- tsec2->exec_sid = tsec1->exec_sid;
- tsec2->create_sid = tsec1->create_sid;
- /* Retain ptracer SID across fork, if any.
- This will be reset by the ptrace hook upon any
- subsequent ptrace_attach operations. */
- tsec2->ptrace_sid = tsec1->ptrace_sid;
- return 0;
- }
- static void selinux_task_free_security(struct task_struct *tsk)
- {
- task_free_security(tsk);
- }
- static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
- {
- /* Since setuid only affects the current process, and
- since the SELinux controls are not based on the Linux
- identity attributes, SELinux does not need to control
- this operation. However, SELinux does control the use
- of the CAP_SETUID and CAP_SETGID capabilities using the
- capable hook. */
- return 0;
- }
- static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
- {
- return secondary_ops->task_post_setuid(id0,id1,id2,flags);
- }
- static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
- {
- /* See the comment for setuid above. */
- return 0;
- }
- static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
- {
- return task_has_perm(current, p, PROCESS__SETPGID);
- }
- static int selinux_task_getpgid(struct task_struct *p)
- {
- return task_has_perm(current, p, PROCESS__GETPGID);
- }
- static int selinux_task_getsid(struct task_struct *p)
- {
- return task_has_perm(current, p, PROCESS__GETSESSION);
- }
- static int selinux_task_setgroups(struct group_info *group_info)
- {
- /* See the comment for setuid above. */
- return 0;
- }
- static int selinux_task_setnice(struct task_struct *p, int nice)
- {
- int rc;
- rc = secondary_ops->task_setnice(p, nice);
- if (rc)
- return rc;
- return task_has_perm(current,p, PROCESS__SETSCHED);
- }
- static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
- {
- struct rlimit *old_rlim = current->signal->rlim + resource;
- int rc;
- rc = secondary_ops->task_setrlimit(resource, new_rlim);
- if (rc)
- return rc;
- /* Control the ability to change the hard limit (whether
- lowering or raising it), so that the hard limit can
- later be used as a safe reset point for the soft limit
- upon context transitions. See selinux_bprm_apply_creds. */
- if (old_rlim->rlim_max != new_rlim->rlim_max)
- return task_has_perm(current, current, PROCESS__SETRLIMIT);
- return 0;
- }
- static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
- {
- return task_has_perm(current, p, PROCESS__SETSCHED);
- }
- static int selinux_task_getscheduler(struct task_struct *p)
- {
- return task_has_perm(current, p, PROCESS__GETSCHED);
- }
- static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
- {
- u32 perm;
- int rc;
- rc = secondary_ops->task_kill(p, info, sig);
- if (rc)
- return rc;
- if (info && ((unsigned long)info == 1 ||
- (unsigned long)info == 2 || SI_FROMKERNEL(info)))
- return 0;
- if (!sig)
- perm = PROCESS__SIGNULL; /* null signal; existence test */
- else
- perm = signal_to_av(sig);
- return task_has_perm(current, p, perm);
- }
- static int selinux_task_prctl(int option,
- unsigned long arg2,
- unsigned long arg3,
- unsigned long arg4,
- unsigned long arg5)
- {
- /* The current prctl operations do not appear to require
- any SELinux controls since they merely observe or modify
- the state of the current process. */
- return 0;
- }
- static int selinux_task_wait(struct task_struct *p)
- {
- u32 perm;
- perm = signal_to_av(p->exit_signal);
- return task_has_perm(p, current, perm);
- }
- static void selinux_task_reparent_to_init(struct task_struct *p)
- {
- struct task_security_struct *tsec;
- secondary_ops->task_reparent_to_init(p);
- tsec = p->security;
- tsec->osid = tsec->sid;
- tsec->sid = SECINITSID_KERNEL;
- return;
- }
- static void selinux_task_to_inode(struct task_struct *p,
- struct inode *inode)
- {
- struct task_security_struct *tsec = p->security;
- struct inode_security_struct *isec = inode->i_security;
- isec->sid = tsec->sid;
- isec->initialized = 1;
- return;
- }
- #ifdef CONFIG_SECURITY_NETWORK
- /* Returns error only if unable to parse addresses */
- static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
- {
- int offset, ihlen, ret = -EINVAL;
- struct iphdr _iph, *ih;
- offset = skb->nh.raw - skb->data;
- ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
- if (ih == NULL)
- goto out;
- ihlen = ih->ihl * 4;
- if (ihlen < sizeof(_iph))
- goto out;
- ad->u.net.v4info.saddr = ih->saddr;
- ad->u.net.v4info.daddr = ih->daddr;
- ret = 0;
- switch (ih->protocol) {
- case IPPROTO_TCP: {
- struct tcphdr _tcph, *th;
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
- offset += ihlen;
- th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
- if (th == NULL)
- break;
- ad->u.net.sport = th->source;
- ad->u.net.dport = th->dest;
- break;
- }
-
- case IPPROTO_UDP: {
- struct udphdr _udph, *uh;
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- offset += ihlen;
- uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
- if (uh == NULL)
- break;
- ad->u.net.sport = uh->source;
- ad->u.net.dport = uh->dest;
- break;
- }
- default:
- break;
- }
- out:
- return ret;
- }
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- /* Returns error only if unable to parse addresses */
- static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
- {
- u8 nexthdr;
- int ret = -EINVAL, offset;
- struct ipv6hdr _ipv6h, *ip6;
- offset = skb->nh.raw - skb->data;
- ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
- if (ip6 == NULL)
- goto out;
- ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
- ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
- ret = 0;
- nexthdr = ip6->nexthdr;
- offset += sizeof(_ipv6h);
- offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
- if (offset < 0)
- goto out;
- switch (nexthdr) {
- case IPPROTO_TCP: {
- struct tcphdr _tcph, *th;
- th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
- if (th == NULL)
- break;
- ad->u.net.sport = th->source;
- ad->u.net.dport = th->dest;
- break;
- }
- case IPPROTO_UDP: {
- struct udphdr _udph, *uh;
- uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
- if (uh == NULL)
- break;
- ad->u.net.sport = uh->source;
- ad->u.net.dport = uh->dest;
- break;
- }
- /* includes fragments */
- default:
- break;
- }
- out:
- return ret;
- }
- #endif /* IPV6 */
- static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
- char **addrp, int *len, int src)
- {
- int ret = 0;
- switch (ad->u.net.family) {
- case PF_INET:
- ret = selinux_parse_skb_ipv4(skb, ad);
- if (ret || !addrp)
- break;
- *len = 4;
- *addrp = (char *)(src ? &ad->u.net.v4info.saddr :
- &ad->u.net.v4info.daddr);
- break;
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case PF_INET6:
- ret = selinux_parse_skb_ipv6(skb, ad);
- if (ret || !addrp)
- break;
- *len = 16;
- *addrp = (char *)(src ? &ad->u.net.v6info.saddr :
- &ad->u.net.v6info.daddr);
- break;
- #endif /* IPV6 */
- default:
- break;
- }
- return ret;
- }
- /* socket security operations */
- static int socket_has_perm(struct task_struct *task, struct socket *sock,
- u32 perms)
- {
- struct inode_security_struct *isec;
- struct task_security_struct *tsec;
- struct avc_audit_data ad;
- int err = 0;
- tsec = task->security;
- isec = SOCK_INODE(sock)->i_security;
- if (isec->sid == SECINITSID_KERNEL)
- goto out;
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.sk = sock->sk;
- err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
- out:
- return err;
- }
- static int selinux_socket_create(int family, int type,
- int protocol, int kern)
- {
- int err = 0;
- struct task_security_struct *tsec;
- if (kern)
- goto out;
- tsec = current->security;
- err = avc_has_perm(tsec->sid, tsec->sid,
- socket_type_to_security_class(family, type,
- protocol), SOCKET__CREATE, NULL);
- out:
- return err;
- }
- static void selinux_socket_post_create(struct socket *sock, int family,
- int type, int protocol, int kern)
- {
- struct inode_security_struct *isec;
- struct task_security_struct *tsec;
- isec = SOCK_INODE(sock)->i_security;
- tsec = current->security;
- isec->sclass = socket_type_to_security_class(family, type, protocol);
- isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
- isec->initialized = 1;
- return;
- }
- /* Range of port numbers used to automatically bind.
- Need to determine whether we should perform a name_bind
- permission check between the socket and the port number. */
- #define ip_local_port_range_0 sysctl_local_port_range[0]
- #define ip_local_port_range_1 sysctl_local_port_range[1]
- static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
- {
- u16 family;
- int err;
- err = socket_has_perm(current, sock, SOCKET__BIND);
- if (err)
- goto out;
- /*
- * If PF_INET or PF_INET6, check name_bind permission for the port.
- */
- family = sock->sk->sk_family;
- if (family == PF_INET || family == PF_INET6) {
- char *addrp;
- struct inode_security_struct *isec;
- struct task_security_struct *tsec;
- struct avc_audit_data ad;
- struct sockaddr_in *addr4 = NULL;
- struct sockaddr_in6 *addr6 = NULL;
- unsigned short snum;
- struct sock *sk = sock->sk;
- u32 sid, node_perm, addrlen;
- tsec = current->security;
- isec = SOCK_INODE(sock)->i_security;
- if (family == PF_INET) {
- addr4 = (struct sockaddr_in *)address;
- snum = ntohs(addr4->sin_port);
- addrlen = sizeof(addr4->sin_addr.s_addr);
- addrp = (char *)&addr4->sin_addr.s_addr;
- } else {
- addr6 = (struct sockaddr_in6 *)address;
- snum = ntohs(addr6->sin6_port);
- addrlen = sizeof(addr6->sin6_addr.s6_addr);
- addrp = (char *)&addr6->sin6_addr.s6_addr;
- }
- if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
- snum > ip_local_port_range_1)) {
- err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, snum, &sid);
- if (err)
- goto out;
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.sport = htons(snum);
- ad.u.net.family = family;
- err = avc_has_perm(isec->sid, sid,
- isec->sclass,
- SOCKET__NAME_BIND, &ad);
- if (err)
- goto out;
- }
-
- switch(sk->sk_protocol) {
- case IPPROTO_TCP:
- node_perm = TCP_SOCKET__NODE_BIND;
- break;
-
- case IPPROTO_UDP:
- node_perm = UDP_SOCKET__NODE_BIND;
- break;
-
- default:
- node_perm = RAWIP_SOCKET__NODE_BIND;
- break;
- }
-
- err = security_node_sid(family, addrp, addrlen, &sid);
- if (err)
- goto out;
-
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.sport = htons(snum);
- ad.u.net.family = family;
- if (family == PF_INET)
- ad.u.net.v4info.saddr = addr4->sin_addr.s_addr;
- else
- ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
- err = avc_has_perm(isec->sid, sid,
- isec->sclass, node_perm, &ad);
- if (err)
- goto out;
- }
- out:
- return err;
- }
- static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
- {
- struct inode_security_struct *isec;
- int err;
- err = socket_has_perm(current, sock, SOCKET__CONNECT);
- if (err)
- return err;
- /*
- * If a TCP socket, check name_connect permission for the port.
- */
- isec = SOCK_INODE(sock)->i_security;
- if (isec->sclass == SECCLASS_TCP_SOCKET) {
- struct sock *sk = sock->sk;
- struct avc_audit_data ad;
- struct sockaddr_in *addr4 = NULL;
- struct sockaddr_in6 *addr6 = NULL;
- unsigned short snum;
- u32 sid;
- if (sk->sk_family == PF_INET) {
- addr4 = (struct sockaddr_in *)address;
- if (addrlen != sizeof(struct sockaddr_in))
- return -EINVAL;
- snum = ntohs(addr4->sin_port);
- } else {
- addr6 = (struct sockaddr_in6 *)address;
- if (addrlen != sizeof(struct sockaddr_in6))
- return -EINVAL;
- snum = ntohs(addr6->sin6_port);
- }
- err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, snum, &sid);
- if (err)
- goto out;
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.dport = htons(snum);
- ad.u.net.family = sk->sk_family;
- err = avc_has_perm(isec->sid, sid, isec->sclass,
- TCP_SOCKET__NAME_CONNECT, &ad);
- if (err)
- goto out;
- }
- out:
- return err;
- }
- static int selinux_socket_listen(struct socket *sock, int backlog)
- {
- return socket_has_perm(current, sock, SOCKET__LISTEN);
- }
- static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
- {
- int err;
- struct inode_security_struct *isec;
- struct inode_security_struct *newisec;
- err = socket_has_perm(current, sock, SOCKET__ACCEPT);
- if (err)
- return err;
- newisec = SOCK_INODE(newsock)->i_security;
- isec = SOCK_INODE(sock)->i_security;
- newisec->sclass = isec->sclass;
- newisec->sid = isec->sid;
- newisec->initialized = 1;
- return 0;
- }
- static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
- int size)
- {
- return socket_has_perm(current, sock, SOCKET__WRITE);
- }
- static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
- int size, int flags)
- {
- return socket_has_perm(current, sock, SOCKET__READ);
- }
- static int selinux_socket_getsockname(struct socket *sock)
- {
- return socket_has_perm(current, sock, SOCKET__GETATTR);
- }
- static int selinux_socket_getpeername(struct socket *sock)
- {
- return socket_has_perm(current, sock, SOCKET__GETATTR);
- }
- static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
- {
- return socket_has_perm(current, sock, SOCKET__SETOPT);
- }
- static int selinux_socket_getsockopt(struct socket *sock, int level,
- int optname)
- {
- return socket_has_perm(current, sock, SOCKET__GETOPT);
- }
- static int selinux_socket_shutdown(struct socket *sock, int how)
- {
- return socket_has_perm(current, sock, SOCKET__SHUTDOWN);
- }
- static int selinux_socket_unix_stream_connect(struct socket *sock,
- struct socket *other,
- struct sock *newsk)
- {
- struct sk_security_struct *ssec;
- struct inode_security_struct *isec;
- struct inode_security_struct *other_isec;
- struct avc_audit_data ad;
- int err;
- err = secondary_ops->unix_stream_connect(sock, other, newsk);
- if (err)
- return err;
- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.sk = other->sk;
- err = avc_has_perm(isec->sid, other_isec->sid,
- isec->sclass,
- UNIX_STREAM_SOCKET__CONNECTTO, &ad);
- if (err)
- return err;
- /* connecting socket */
- ssec = sock->sk->sk_security;
- ssec->peer_sid = other_isec->sid;
-
- /* server child socket */
- ssec = newsk->sk_security;
- ssec->peer_sid = isec->sid;
-
- return 0;
- }
- static int selinux_socket_unix_may_send(struct socket *sock,
- struct socket *other)
- {
- struct inode_security_struct *isec;
- struct inode_security_struct *other_isec;
- struct avc_audit_data ad;
- int err;
- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
- AVC_AUDIT_DATA_INIT(&ad,NET);
- ad.u.net.sk = other->sk;
- err = avc_has_perm(isec->sid, other_isec->sid,
- isec->sclass, SOCKET__SENDTO, &ad);
- if (err)
- return err;
- return 0;
- }
- static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
- {
- u16 family;
- char *addrp;
- int len, err = 0;
- u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
- u32 sock_sid = 0;
- u16 sock_class = 0;
- struct socket *sock;
- struct net_device *dev;
- struct avc_audit_data ad;
- family = sk->sk_family;
- if (family != PF_INET && family != PF_INET6)
- goto out;
- /* Handle mapped IPv4 packets arriving via IPv6 sockets */
- if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
- family = PF_INET;
- read_lock_bh(&sk->sk_callback_lock);
- sock = sk->sk_socket;
- if (sock) {
- struct inode *inode;
- inode = SOCK_INODE(sock);
- if (inode) {
- struct inode_security_struct *isec;
- isec = inode->i_security;
- sock_sid = isec->sid;
- sock_class = isec->sclass;
- }
- }
- read_unlock_bh(&sk->sk_callback_lock);
- if (!sock_sid)
- goto out;
- dev = skb->dev;
- if (!dev)
- goto out;
- err = sel_netif_sids(dev, &if_sid, NULL);
- if (err)
- goto out;
- switch (sock_class) {
- case SECCLASS_UDP_SOCKET:
- netif_perm = NETIF__UDP_RECV;
- node_perm = NODE__UDP_RECV;
- recv_perm = UDP_SOCKET__RECV_MSG;
- break;
-
- case SECCLASS_TCP_SOCKET:
- netif_perm = NETIF__TCP_RECV;
- node_perm = NODE__TCP_RECV;
- recv_perm = TCP_SOCKET__RECV_MSG;
- break;
-
- default:
- netif_perm = NETIF__RAWIP_RECV;
- node_perm = NODE__RAWIP_RECV;
- break;
- }
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
- err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
- if (err)
- goto out;
- err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
- if (err)
- goto out;
-
- /* Fixme: this lookup is inefficient */
- err = security_node_sid(family, addrp, len, &node_sid);
- if (err)
- goto out;
-
- err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad);
- if (err)
- goto out;
- if (recv_perm) {
- u32 port_sid;
- /* Fixme: make this more efficient */
- err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, ntohs(ad.u.net.sport),
- &port_sid);
- if (err)
- goto out;
- err = avc_has_perm(sock_sid, port_sid,
- sock_class, recv_perm, &ad);
- }
- out:
- return err;
- }
- static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
- int __user *optlen, unsigned len)
- {
- int err = 0;
- char *scontext;
- u32 scontext_len;
- struct sk_security_struct *ssec;
- struct inode_security_struct *isec;
- isec = SOCK_INODE(sock)->i_security;
- if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
- err = -ENOPROTOOPT;
- goto out;
- }
- ssec = sock->sk->sk_security;
-
- err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
- if (err)
- goto out;
- if (scontext_len > len) {
- err = -ERANGE;
- goto out_len;
- }
- if (copy_to_user(optval, scontext, scontext_len))
- err = -EFAULT;
- out_len:
- if (put_user(scontext_len, optlen))
- err = -EFAULT;
- kfree(scontext);
- out:
- return err;
- }
- static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
- {
- return sk_alloc_security(sk, family, priority);
- }
- static void selinux_sk_free_security(struct sock *sk)
- {
- sk_free_security(sk);
- }
- static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
- {
- int err = 0;
- u32 perm;
- struct nlmsghdr *nlh;
- struct socket *sock = sk->sk_socket;
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
-
- if (skb->len < NLMSG_SPACE(0)) {
- err = -EINVAL;
- goto out;
- }
- nlh = (struct nlmsghdr *)skb->data;
-
- err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
- if (err) {
- if (err == -EINVAL) {
- audit_log(current->audit_context,
- "SELinux: unrecognized netlink message"
- " type=%hu for sclass=%hu\n",
- nlh->nlmsg_type, isec->sclass);
- if (!selinux_enforcing)
- err = 0;
- }
- /* Ignore */
- if (err == -ENOENT)
- err = 0;
- goto out;
- }
- err = socket_has_perm(current, sock, perm);
- out:
- return err;
- }
- #ifdef CONFIG_NETFILTER
- static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
- struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *),
- u16 family)
- {
- char *addrp;
- int len, err = NF_ACCEPT;
- u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
- struct sock *sk;
- struct socket *sock;
- struct inode *inode;
- struct sk_buff *skb = *pskb;
- struct inode_security_struct *isec;
- struct avc_audit_data ad;
- struct net_device *dev = (struct net_device *)out;
-
- sk = skb->sk;
- if (!sk)
- goto out;
-
- sock = sk->sk_socket;
- if (!sock)
- goto out;
-
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
- err = sel_netif_sids(dev, &if_sid, NULL);
- if (err)
- goto out;
- isec = inode->i_security;
-
- switch (isec->sclass) {
- case SECCLASS_UDP_SOCKET:
- netif_perm = NETIF__UDP_SEND;
- node_perm = NODE__UDP_SEND;
- send_perm = UDP_SOCKET__SEND_MSG;
- break;
-
- case SECCLASS_TCP_SOCKET:
- netif_perm = NETIF__TCP_SEND;
- node_perm = NODE__TCP_SEND;
- send_perm = TCP_SOCKET__SEND_MSG;
- break;
-
- default:
- netif_perm = NETIF__RAWIP_SEND;
- node_perm = NODE__RAWIP_SEND;
- break;
- }
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
- err = selinux_parse_skb(skb, &ad, &addrp,
- &len, 0) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
- err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
- netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
-
- /* Fixme: this lookup is inefficient */
- err = security_node_sid(family, addrp, len,
- &node_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
-
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
- node_perm, &ad) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
- if (send_perm) {
- u32 port_sid;
-
- /* Fixme: make this more efficient */
- err = security_port_sid(sk->sk_family,
- sk->sk_type,
- sk->sk_protocol,
- ntohs(ad.u.net.dport),
- &port_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
- err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- send_perm, &ad) ? NF_DROP : NF_ACCEPT;
- }
- out:
- return err;
- }
- static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
- struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
- {
- return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
- }
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
- struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
- {
- return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
- }
- #endif /* IPV6 */
- #endif /* CONFIG_NETFILTER */
- #else
- static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
- {
- return 0;
- }
- #endif /* CONFIG_SECURITY_NETWORK */
- static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
- {
- struct task_security_struct *tsec;
- struct av_decision avd;
- int err;
- err = secondary_ops->netlink_send(sk, skb);
- if (err)
- return err;
- tsec = current->security;
- avd.allowed = 0;
- avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY, ~0, &avd);
- cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
- if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
- err = selinux_nlmsg_perm(sk, skb);
- return err;
- }
- static int selinux_netlink_recv(struct sk_buff *skb)
- {
- if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
- return -EPERM;
- return 0;
- }
- static int ipc_alloc_security(struct task_struct *task,
- struct kern_ipc_perm *perm,
- u16 sclass)
- {
- struct task_security_struct *tsec = task->security;
- struct ipc_security_struct *isec;
- isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
- if (!isec)
- return -ENOMEM;
- memset(isec, 0, sizeof(struct ipc_security_struct));
- isec->magic = SELINUX_MAGIC;
- isec->sclass = sclass;
- isec->ipc_perm = perm;
- if (tsec) {
- isec->sid = tsec->sid;
- } else {
- isec->sid = SECINITSID_UNLABELED;
- }
- perm->security = isec;
- return 0;
- }
- static void ipc_free_security(struct kern_ipc_perm *perm)
- {
- struct ipc_security_struct *isec = perm->security;
- if (!isec || isec->magic != SELINUX_MAGIC)
- return;
- perm->security = NULL;
- kfree(isec);
- }
- static int msg_msg_alloc_security(struct msg_msg *msg)
- {
- struct msg_security_struct *msec;
- msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
- if (!msec)
- return -ENOMEM;
- memset(msec, 0, sizeof(struct msg_security_struct));
- msec->magic = SELINUX_MAGIC;
- msec->msg = msg;
- msec->sid = SECINITSID_UNLABELED;
- msg->security = msec;
- return 0;
- }
- static void msg_msg_free_security(struct msg_msg *msg)
- {
- struct msg_security_struct *msec = msg->security;
- if (!msec || msec->magic != SELINUX_MAGIC)
- return;
- msg->security = NULL;
- kfree(msec);
- }
- static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
- u16 sclass, u32 perms)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- tsec = current->security;
- isec = ipc_perms->security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad);
- }
- static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
- {
- return msg_msg_alloc_security(msg);
- }
- static void selinux_msg_msg_free_security(struct msg_msg *msg)
- {
- msg_msg_free_security(msg);
- }
- /* message queue security operations */
- static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- int rc;
- rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
- if (rc)
- return rc;
- tsec = current->security;
- isec = msq->q_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__CREATE, &ad);
- if (rc) {
- ipc_free_security(&msq->q_perm);
- return rc;
- }
- return 0;
- }
- static void selinux_msg_queue_free_security(struct msg_queue *msq)
- {
- ipc_free_security(&msq->q_perm);
- }
- static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- tsec = current->security;
- isec = msq->q_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = msq->q_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__ASSOCIATE, &ad);
- }
- static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
- {
- int err;
- int perms;
- switch(cmd) {
- case IPC_INFO:
- case MSG_INFO:
- /* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
- case IPC_STAT:
- case MSG_STAT:
- perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
- break;
- case IPC_SET:
- perms = MSGQ__SETATTR;
- break;
- case IPC_RMID:
- perms = MSGQ__DESTROY;
- break;
- default:
- return 0;
- }
- err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms);
- return err;
- }
- static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct msg_security_struct *msec;
- struct avc_audit_data ad;
- int rc;
- tsec = current->security;
- isec = msq->q_perm.security;
- msec = msg->security;
- /*
- * First time through, need to assign label to the message
- */
- if (msec->sid == SECINITSID_UNLABELED) {
- /*
- * Compute new sid based on current process and
- * message queue this message will be stored in
- */
- rc = security_transition_sid(tsec->sid,
- isec->sid,
- SECCLASS_MSG,
- &msec->sid);
- if (rc)
- return rc;
- }
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = msq->q_perm.key;
- /* Can this process write to the queue? */
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__WRITE, &ad);
- if (!rc)
- /* Can this process send the message */
- rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__SEND, &ad);
- if (!rc)
- /* Can the message be put in the queue? */
- rc = avc_has_perm(msec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
- return rc;
- }
- static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
- struct task_struct *target,
- long type, int mode)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct msg_security_struct *msec;
- struct avc_audit_data ad;
- int rc;
- tsec = target->security;
- isec = msq->q_perm.security;
- msec = msg->security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__READ, &ad);
- if (!rc)
- rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__RECEIVE, &ad);
- return rc;
- }
- /* Shared Memory security operations */
- static int selinux_shm_alloc_security(struct shmid_kernel *shp)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- int rc;
- rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
- if (rc)
- return rc;
- tsec = current->security;
- isec = shp->shm_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = shp->shm_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__CREATE, &ad);
- if (rc) {
- ipc_free_security(&shp->shm_perm);
- return rc;
- }
- return 0;
- }
- static void selinux_shm_free_security(struct shmid_kernel *shp)
- {
- ipc_free_security(&shp->shm_perm);
- }
- static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- tsec = current->security;
- isec = shp->shm_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = shp->shm_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__ASSOCIATE, &ad);
- }
- /* Note, at this point, shp is locked down */
- static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
- {
- int perms;
- int err;
- switch(cmd) {
- case IPC_INFO:
- case SHM_INFO:
- /* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
- case IPC_STAT:
- case SHM_STAT:
- perms = SHM__GETATTR | SHM__ASSOCIATE;
- break;
- case IPC_SET:
- perms = SHM__SETATTR;
- break;
- case SHM_LOCK:
- case SHM_UNLOCK:
- perms = SHM__LOCK;
- break;
- case IPC_RMID:
- perms = SHM__DESTROY;
- break;
- default:
- return 0;
- }
- err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
- return err;
- }
- static int selinux_shm_shmat(struct shmid_kernel *shp,
- char __user *shmaddr, int shmflg)
- {
- u32 perms;
- int rc;
- rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
- if (rc)
- return rc;
- if (shmflg & SHM_RDONLY)
- perms = SHM__READ;
- else
- perms = SHM__READ | SHM__WRITE;
- return ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
- }
- /* Semaphore security operations */
- static int selinux_sem_alloc_security(struct sem_array *sma)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- int rc;
- rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
- if (rc)
- return rc;
- tsec = current->security;
- isec = sma->sem_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = sma->sem_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__CREATE, &ad);
- if (rc) {
- ipc_free_security(&sma->sem_perm);
- return rc;
- }
- return 0;
- }
- static void selinux_sem_free_security(struct sem_array *sma)
- {
- ipc_free_security(&sma->sem_perm);
- }
- static int selinux_sem_associate(struct sem_array *sma, int semflg)
- {
- struct task_security_struct *tsec;
- struct ipc_security_struct *isec;
- struct avc_audit_data ad;
- tsec = current->security;
- isec = sma->sem_perm.security;
- AVC_AUDIT_DATA_INIT(&ad, IPC);
- ad.u.ipc_id = sma->sem_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__ASSOCIATE, &ad);
- }
- /* Note, at this point, sma is locked down */
- static int selinux_sem_semctl(struct sem_array *sma, int cmd)
- {
- int err;
- u32 perms;
- switch(cmd) {
- case IPC_INFO:
- case SEM_INFO:
- /* No specific object, just general system-wide information. */
- return task_has_system(current, SYSTEM__IPC_INFO);
- case GETPID:
- case GETNCNT:
- case GETZCNT:
- perms = SEM__GETATTR;
- break;
- case GETVAL:
- case GETALL:
- perms = SEM__READ;
- break;
- case SETVAL:
- case SETALL:
- perms = SEM__WRITE;
- break;
- case IPC_RMID:
- perms = SEM__DESTROY;
- break;
- case IPC_SET:
- perms = SEM__SETATTR;
- break;
- case IPC_STAT:
- case SEM_STAT:
- perms = SEM__GETATTR | SEM__ASSOCIATE;
- break;
- default:
- return 0;
- }
- err = ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
- return err;
- }
- static int selinux_sem_semop(struct sem_array *sma,
- struct sembuf *sops, unsigned nsops, int alter)
- {
- u32 perms;
- if (alter)
- perms = SEM__READ | SEM__WRITE;
- else
- perms = SEM__READ;
- return ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
- }
- static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
- {
- struct ipc_security_struct *isec = ipcp->security;
- u16 sclass = SECCLASS_IPC;
- u32 av = 0;
- if (isec && isec->magic == SELINUX_MAGIC)
- sclass = isec->sclass;
- av = 0;
- if (flag & S_IRUGO)
- av |= IPC__UNIX_READ;
- if (flag & S_IWUGO)
- av |= IPC__UNIX_WRITE;
- if (av == 0)
- return 0;
- return ipc_has_perm(ipcp, sclass, av);
- }
- /* module stacking operations */
- static int selinux_register_security (const char *name, struct security_operations *ops)
- {
- if (secondary_ops != original_ops) {
- printk(KERN_INFO "%s: There is already a secondary security "
- "module registered.\n", __FUNCTION__);
- return -EINVAL;
- }
- secondary_ops = ops;
- printk(KERN_INFO "%s: Registering secondary module %s\n",
- __FUNCTION__,
- name);
- return 0;
- }
- static int selinux_unregister_security (const char *name, struct security_operations *ops)
- {
- if (ops != secondary_ops) {
- printk (KERN_INFO "%s: trying to unregister a security module "
- "that is not registered.\n", __FUNCTION__);
- return -EINVAL;
- }
- secondary_ops = original_ops;
- return 0;
- }
- static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
- {
- if (inode)
- inode_doinit_with_dentry(inode, dentry);
- }
- static int selinux_getprocattr(struct task_struct *p,
- char *name, void *value, size_t size)
- {
- struct task_security_struct *tsec;
- u32 sid, len;
- char *context;
- int error;
- if (current != p) {
- error = task_has_perm(current, p, PROCESS__GETATTR);
- if (error)
- return error;
- }
- if (!size)
- return -ERANGE;
- tsec = p->security;
- if (!strcmp(name, "current"))
- sid = tsec->sid;
- else if (!strcmp(name, "prev"))
- sid = tsec->osid;
- else if (!strcmp(name, "exec"))
- sid = tsec->exec_sid;
- else if (!strcmp(name, "fscreate"))
- sid = tsec->create_sid;
- else
- return -EINVAL;
- if (!sid)
- return 0;
- error = security_sid_to_context(sid, &context, &len);
- if (error)
- return error;
- if (len > size) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(value, context, len);
- kfree(context);
- return len;
- }
- static int selinux_setprocattr(struct task_struct *p,
- char *name, void *value, size_t size)
- {
- struct task_security_struct *tsec;
- u32 sid = 0;
- int error;
- char *str = value;
- if (current != p) {
- /* SELinux only allows a process to change its own
- security attributes. */
- return -EACCES;
- }
- /*
- * Basic control over ability to set these attributes at all.
- * current == p, but we'll pass them separately in case the
- * above restriction is ever removed.
- */
- if (!strcmp(name, "exec"))
- error = task_has_perm(current, p, PROCESS__SETEXEC);
- else if (!strcmp(name, "fscreate"))
- error = task_has_perm(current, p, PROCESS__SETFSCREATE);
- else if (!strcmp(name, "current"))
- error = task_has_perm(current, p, PROCESS__SETCURRENT);
- else
- error = -EINVAL;
- if (error)
- return error;
- /* Obtain a SID for the context, if one was specified. */
- if (size && str[1] && str[1] != '\n') {
- if (str[size-1] == '\n') {
- str[size-1] = 0;
- size--;
- }
- error = security_context_to_sid(value, size, &sid);
- if (error)
- return error;
- }
- /* Permission checking based on the specified context is
- performed during the actual operation (execve,
- open/mkdir/...), when we know the full context of the
- operation. See selinux_bprm_set_security for the execve
- checks and may_create for the file creation checks. The
- operation will then fail if the context is not permitted. */
- tsec = p->security;
- if (!strcmp(name, "exec"))
- tsec->exec_sid = sid;
- else if (!strcmp(name, "fscreate"))
- tsec->create_sid = sid;
- else if (!strcmp(name, "current")) {
- struct av_decision avd;
- if (sid == 0)
- return -EINVAL;
- /* Only allow single threaded processes to change context */
- if (atomic_read(&p->mm->mm_users) != 1) {
- struct task_struct *g, *t;
- struct mm_struct *mm = p->mm;
- read_lock(&tasklist_lock);
- do_each_thread(g, t)
- if (t->mm == mm && t != p) {
- read_unlock(&tasklist_lock);
- return -EPERM;
- }
- while_each_thread(g, t);
- read_unlock(&tasklist_lock);
- }
- /* Check permissions for the transition. */
- error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__DYNTRANSITION, NULL);
- if (error)
- return error;
- /* Check for ptracing, and update the task SID if ok.
- Otherwise, leave SID unchanged and fail. */
- task_lock(p);
- if (p->ptrace & PT_PTRACED) {
- error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
- SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd);
- if (!error)
- tsec->sid = sid;
- task_unlock(p);
- avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, error, NULL);
- if (error)
- return error;
- } else {
- tsec->sid = sid;
- task_unlock(p);
- }
- }
- else
- return -EINVAL;
- return size;
- }
- static struct security_operations selinux_ops = {
- .ptrace = selinux_ptrace,
- .capget = selinux_capget,
- .capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
- .sysctl = selinux_sysctl,
- .capable = selinux_capable,
- .quotactl = selinux_quotactl,
- .quota_on = selinux_quota_on,
- .syslog = selinux_syslog,
- .vm_enough_memory = selinux_vm_enough_memory,
- .netlink_send = selinux_netlink_send,
- .netlink_recv = selinux_netlink_recv,
- .bprm_alloc_security = selinux_bprm_alloc_security,
- .bprm_free_security = selinux_bprm_free_security,
- .bprm_apply_creds = selinux_bprm_apply_creds,
- .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
- .bprm_set_security = selinux_bprm_set_security,
- .bprm_check_security = selinux_bprm_check_security,
- .bprm_secureexec = selinux_bprm_secureexec,
- .sb_alloc_security = selinux_sb_alloc_security,
- .sb_free_security = selinux_sb_free_security,
- .sb_copy_data = selinux_sb_copy_data,
- .sb_kern_mount = selinux_sb_kern_mount,
- .sb_statfs = selinux_sb_statfs,
- .sb_mount = selinux_mount,
- .sb_umount = selinux_umount,
- .inode_alloc_security = selinux_inode_alloc_security,
- .inode_free_security = selinux_inode_free_security,
- .inode_create = selinux_inode_create,
- .inode_post_create = selinux_inode_post_create,
- .inode_link = selinux_inode_link,
- .inode_post_link = selinux_inode_post_link,
- .inode_unlink = selinux_inode_unlink,
- .inode_symlink = selinux_inode_symlink,
- .inode_post_symlink = selinux_inode_post_symlink,
- .inode_mkdir = selinux_inode_mkdir,
- .inode_post_mkdir = selinux_inode_post_mkdir,
- .inode_rmdir = selinux_inode_rmdir,
- .inode_mknod = selinux_inode_mknod,
- .inode_post_mknod = selinux_inode_post_mknod,
- .inode_rename = selinux_inode_rename,
- .inode_post_rename = selinux_inode_post_rename,
- .inode_readlink = selinux_inode_readlink,
- .inode_follow_link = selinux_inode_follow_link,
- .inode_permission = selinux_inode_permission,
- .inode_setattr = selinux_inode_setattr,
- .inode_getattr = selinux_inode_getattr,
- .inode_setxattr = selinux_inode_setxattr,
- .inode_post_setxattr = selinux_inode_post_setxattr,
- .inode_getxattr = selinux_inode_getxattr,
- .inode_listxattr = selinux_inode_listxattr,
- .inode_removexattr = selinux_inode_removexattr,
- .inode_getsecurity = selinux_inode_getsecurity,
- .inode_setsecurity = selinux_inode_setsecurity,
- .inode_listsecurity = selinux_inode_listsecurity,
- .file_permission = selinux_file_permission,
- .file_alloc_security = selinux_file_alloc_security,
- .file_free_security = selinux_file_free_security,
- .file_ioctl = selinux_file_ioctl,
- .file_mmap = selinux_file_mmap,
- .file_mprotect = selinux_file_mprotect,
- .file_lock = selinux_file_lock,
- .file_fcntl = selinux_file_fcntl,
- .file_set_fowner = selinux_file_set_fowner,
- .file_send_sigiotask = selinux_file_send_sigiotask,
- .file_receive = selinux_file_receive,
- .task_create = selinux_task_create,
- .task_alloc_security = selinux_task_alloc_security,
- .task_free_security = selinux_task_free_security,
- .task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
- .task_setgid = selinux_task_setgid,
- .task_setpgid = selinux_task_setpgid,
- .task_getpgid = selinux_task_getpgid,
- .task_getsid = selinux_task_getsid,
- .task_setgroups = selinux_task_setgroups,
- .task_setnice = selinux_task_setnice,
- .task_setrlimit = selinux_task_setrlimit,
- .task_setscheduler = selinux_task_setscheduler,
- .task_getscheduler = selinux_task_getscheduler,
- .task_kill = selinux_task_kill,
- .task_wait = selinux_task_wait,
- .task_prctl = selinux_task_prctl,
- .task_reparent_to_init = selinux_task_reparent_to_init,
- .task_to_inode = selinux_task_to_inode,
- .ipc_permission = selinux_ipc_permission,
- .msg_msg_alloc_security = selinux_msg_msg_alloc_security,
- .msg_msg_free_security = selinux_msg_msg_free_security,
- .msg_queue_alloc_security = selinux_msg_queue_alloc_security,
- .msg_queue_free_security = selinux_msg_queue_free_security,
- .msg_queue_associate = selinux_msg_queue_associate,
- .msg_queue_msgctl = selinux_msg_queue_msgctl,
- .msg_queue_msgsnd = selinux_msg_queue_msgsnd,
- .msg_queue_msgrcv = selinux_msg_queue_msgrcv,
- .shm_alloc_security = selinux_shm_alloc_security,
- .shm_free_security = selinux_shm_free_security,
- .shm_associate = selinux_shm_associate,
- .shm_shmctl = selinux_shm_shmctl,
- .shm_shmat = selinux_shm_shmat,
- .sem_alloc_security = selinux_sem_alloc_security,
- .sem_free_security = selinux_sem_free_security,
- .sem_associate = selinux_sem_associate,
- .sem_semctl = selinux_sem_semctl,
- .sem_semop = selinux_sem_semop,
- .register_security = selinux_register_security,
- .unregister_security = selinux_unregister_security,
- .d_instantiate = selinux_d_instantiate,
- .getprocattr = selinux_getprocattr,
- .setprocattr = selinux_setprocattr,
- #ifdef CONFIG_SECURITY_NETWORK
- .unix_stream_connect = selinux_socket_unix_stream_connect,
- .unix_may_send = selinux_socket_unix_may_send,
- .socket_create = selinux_socket_create,
- .socket_post_create = selinux_socket_post_create,
- .socket_bind = selinux_socket_bind,
- .socket_connect = selinux_socket_connect,
- .socket_listen = selinux_socket_listen,
- .socket_accept = selinux_socket_accept,
- .socket_sendmsg = selinux_socket_sendmsg,
- .socket_recvmsg = selinux_socket_recvmsg,
- .socket_getsockname = selinux_socket_getsockname,
- .socket_getpeername = selinux_socket_getpeername,
- .socket_getsockopt = selinux_socket_getsockopt,
- .socket_setsockopt = selinux_socket_setsockopt,
- .socket_shutdown = selinux_socket_shutdown,
- .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb,
- .socket_getpeersec = selinux_socket_getpeersec,
- .sk_alloc_security = selinux_sk_alloc_security,
- .sk_free_security = selinux_sk_free_security,
- #endif
- };
- static __init int selinux_init(void)
- {
- struct task_security_struct *tsec;
- if (!selinux_enabled) {
- printk(KERN_INFO "SELinux: Disabled at boot.\n");
- return 0;
- }
- printk(KERN_INFO "SELinux: Initializing.\n");
- /* Set the security state for the initial task. */
- if (task_alloc_security(current))
- panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->security;
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
- avc_init();
- original_ops = secondary_ops = security_ops;
- if (!secondary_ops)
- panic ("SELinux: No initial security operations\n");
- if (register_security (&selinux_ops))
- panic("SELinux: Unable to register with kernel.\n");
- if (selinux_enforcing) {
- printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
- } else {
- printk(KERN_INFO "SELinux: Starting in permissive mode\n");
- }
- return 0;
- }
- void selinux_complete_init(void)
- {
- printk(KERN_INFO "SELinux: Completing initialization.\n");
- /* Set up any superblocks initialized prior to the policy load. */
- printk(KERN_INFO "SELinux: Setting up existing superblocks.\n");
- spin_lock(&sb_security_lock);
- next_sb:
- if (!list_empty(&superblock_security_head)) {
- struct superblock_security_struct *sbsec =
- list_entry(superblock_security_head.next,
- struct superblock_security_struct,
- list);
- struct super_block *sb = sbsec->sb;
- spin_lock(&sb_lock);
- sb->s_count++;
- spin_unlock(&sb_lock);
- spin_unlock(&sb_security_lock);
- down_read(&sb->s_umount);
- if (sb->s_root)
- superblock_doinit(sb, NULL);
- drop_super(sb);
- spin_lock(&sb_security_lock);
- list_del_init(&sbsec->list);
- goto next_sb;
- }
- spin_unlock(&sb_security_lock);
- }
- /* SELinux requires early initialization in order to label
- all processes and objects when they are created. */
- security_initcall(selinux_init);
- #if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER)
- static struct nf_hook_ops selinux_ipv4_op = {
- .hook = selinux_ipv4_postroute_last,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_POST_ROUTING,
- .priority = NF_IP_PRI_SELINUX_LAST,
- };
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- static struct nf_hook_ops selinux_ipv6_op = {
- .hook = selinux_ipv6_postroute_last,
- .owner = THIS_MODULE,
- .pf = PF_INET6,
- .hooknum = NF_IP6_POST_ROUTING,
- .priority = NF_IP6_PRI_SELINUX_LAST,
- };
- #endif /* IPV6 */
- static int __init selinux_nf_ip_init(void)
- {
- int err = 0;
- if (!selinux_enabled)
- goto out;
-
- printk(KERN_INFO "SELinux: Registering netfilter hooks\n");
-
- err = nf_register_hook(&selinux_ipv4_op);
- if (err)
- panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- err = nf_register_hook(&selinux_ipv6_op);
- if (err)
- panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
- #endif /* IPV6 */
- out:
- return err;
- }
- __initcall(selinux_nf_ip_init);
- #ifdef CONFIG_SECURITY_SELINUX_DISABLE
- static void selinux_nf_ip_exit(void)
- {
- printk(KERN_INFO "SELinux: Unregistering netfilter hooks\n");
- nf_unregister_hook(&selinux_ipv4_op);
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- nf_unregister_hook(&selinux_ipv6_op);
- #endif /* IPV6 */
- }
- #endif
- #else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
- #ifdef CONFIG_SECURITY_SELINUX_DISABLE
- #define selinux_nf_ip_exit()
- #endif
- #endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
- #ifdef CONFIG_SECURITY_SELINUX_DISABLE
- int selinux_disable(void)
- {
- extern void exit_sel_fs(void);
- static int selinux_disabled = 0;
- if (ss_initialized) {
- /* Not permitted after initial policy load. */
- return -EINVAL;
- }
- if (selinux_disabled) {
- /* Only do this once. */
- return -EINVAL;
- }
- printk(KERN_INFO "SELinux: Disabled at runtime.\n");
- selinux_disabled = 1;
- /* Reset security_ops to the secondary module, dummy or capability. */
- security_ops = secondary_ops;
- /* Unregister netfilter hooks. */
- selinux_nf_ip_exit();
- /* Unregister selinuxfs. */
- exit_sel_fs();
- return 0;
- }
- #endif
|