/*1* Copyright (C) 2007 Casey Schaufler <[email protected]>2*3* This program is free software; you can redistribute it and/or modify4* it under the terms of the GNU General Public License as published by5* the Free Software Foundation, version 2.6*7* Author:8* Casey Schaufler <[email protected]>9*10*/1112#include <linux/types.h>13#include <linux/slab.h>14#include <linux/fs.h>15#include <linux/sched.h>16#include "smack.h"1718struct smack_known smack_known_huh = {19.smk_known = "?",20.smk_secid = 2,21.smk_cipso = NULL,22};2324struct smack_known smack_known_hat = {25.smk_known = "^",26.smk_secid = 3,27.smk_cipso = NULL,28};2930struct smack_known smack_known_star = {31.smk_known = "*",32.smk_secid = 4,33.smk_cipso = NULL,34};3536struct smack_known smack_known_floor = {37.smk_known = "_",38.smk_secid = 5,39.smk_cipso = NULL,40};4142struct smack_known smack_known_invalid = {43.smk_known = "",44.smk_secid = 6,45.smk_cipso = NULL,46};4748struct smack_known smack_known_web = {49.smk_known = "@",50.smk_secid = 7,51.smk_cipso = NULL,52};5354LIST_HEAD(smack_known_list);5556/*57* The initial value needs to be bigger than any of the58* known values above.59*/60static u32 smack_next_secid = 10;6162/*63* what events do we log64* can be overwritten at run-time by /smack/logging65*/66int log_policy = SMACK_AUDIT_DENIED;6768/**69* smk_access_entry - look up matching access rule70* @subject_label: a pointer to the subject's Smack label71* @object_label: a pointer to the object's Smack label72* @rule_list: the list of rules to search73*74* This function looks up the subject/object pair in the75* access rule list and returns the access mode. If no76* entry is found returns -ENOENT.77*78* NOTE:79* Even though Smack labels are usually shared on smack_list80* labels that come in off the network can't be imported81* and added to the list for locking reasons.82*83* Therefore, it is necessary to check the contents of the labels,84* not just the pointer values. Of course, in most cases the labels85* will be on the list, so checking the pointers may be a worthwhile86* optimization.87*/88int smk_access_entry(char *subject_label, char *object_label,89struct list_head *rule_list)90{91int may = -ENOENT;92struct smack_rule *srp;9394list_for_each_entry_rcu(srp, rule_list, list) {95if (srp->smk_subject == subject_label ||96strcmp(srp->smk_subject, subject_label) == 0) {97if (srp->smk_object == object_label ||98strcmp(srp->smk_object, object_label) == 0) {99may = srp->smk_access;100break;101}102}103}104105return may;106}107108/**109* smk_access - determine if a subject has a specific access to an object110* @subject_label: a pointer to the subject's Smack label111* @object_label: a pointer to the object's Smack label112* @request: the access requested, in "MAY" format113* @a : a pointer to the audit data114*115* This function looks up the subject/object pair in the116* access rule list and returns 0 if the access is permitted,117* non zero otherwise.118*119* Even though Smack labels are usually shared on smack_list120* labels that come in off the network can't be imported121* and added to the list for locking reasons.122*123* Therefore, it is necessary to check the contents of the labels,124* not just the pointer values. Of course, in most cases the labels125* will be on the list, so checking the pointers may be a worthwhile126* optimization.127*/128int smk_access(char *subject_label, char *object_label, int request,129struct smk_audit_info *a)130{131int may = MAY_NOT;132int rc = 0;133134/*135* Hardcoded comparisons.136*137* A star subject can't access any object.138*/139if (subject_label == smack_known_star.smk_known ||140strcmp(subject_label, smack_known_star.smk_known) == 0) {141rc = -EACCES;142goto out_audit;143}144/*145* An internet object can be accessed by any subject.146* Tasks cannot be assigned the internet label.147* An internet subject can access any object.148*/149if (object_label == smack_known_web.smk_known ||150subject_label == smack_known_web.smk_known ||151strcmp(object_label, smack_known_web.smk_known) == 0 ||152strcmp(subject_label, smack_known_web.smk_known) == 0)153goto out_audit;154/*155* A star object can be accessed by any subject.156*/157if (object_label == smack_known_star.smk_known ||158strcmp(object_label, smack_known_star.smk_known) == 0)159goto out_audit;160/*161* An object can be accessed in any way by a subject162* with the same label.163*/164if (subject_label == object_label ||165strcmp(subject_label, object_label) == 0)166goto out_audit;167/*168* A hat subject can read any object.169* A floor object can be read by any subject.170*/171if ((request & MAY_ANYREAD) == request) {172if (object_label == smack_known_floor.smk_known ||173strcmp(object_label, smack_known_floor.smk_known) == 0)174goto out_audit;175if (subject_label == smack_known_hat.smk_known ||176strcmp(subject_label, smack_known_hat.smk_known) == 0)177goto out_audit;178}179/*180* Beyond here an explicit relationship is required.181* If the requested access is contained in the available182* access (e.g. read is included in readwrite) it's183* good. A negative response from smk_access_entry()184* indicates there is no entry for this pair.185*/186rcu_read_lock();187may = smk_access_entry(subject_label, object_label, &smack_rule_list);188rcu_read_unlock();189190if (may > 0 && (request & may) == request)191goto out_audit;192193rc = -EACCES;194out_audit:195#ifdef CONFIG_AUDIT196if (a)197smack_log(subject_label, object_label, request, rc, a);198#endif199return rc;200}201202/**203* smk_curacc - determine if current has a specific access to an object204* @obj_label: a pointer to the object's Smack label205* @mode: the access requested, in "MAY" format206* @a : common audit data207*208* This function checks the current subject label/object label pair209* in the access rule list and returns 0 if the access is permitted,210* non zero otherwise. It allows that current may have the capability211* to override the rules.212*/213int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)214{215struct task_smack *tsp = current_security();216char *sp = smk_of_task(tsp);217int may;218int rc;219220/*221* Check the global rule list222*/223rc = smk_access(sp, obj_label, mode, NULL);224if (rc == 0) {225/*226* If there is an entry in the task's rule list227* it can further restrict access.228*/229may = smk_access_entry(sp, obj_label, &tsp->smk_rules);230if (may < 0)231goto out_audit;232if ((mode & may) == mode)233goto out_audit;234rc = -EACCES;235}236237/*238* Return if a specific label has been designated as the239* only one that gets privilege and current does not240* have that label.241*/242if (smack_onlycap != NULL && smack_onlycap != sp)243goto out_audit;244245if (capable(CAP_MAC_OVERRIDE))246rc = 0;247248out_audit:249#ifdef CONFIG_AUDIT250if (a)251smack_log(sp, obj_label, mode, rc, a);252#endif253return rc;254}255256#ifdef CONFIG_AUDIT257/**258* smack_str_from_perm : helper to transalate an int to a259* readable string260* @string : the string to fill261* @access : the int262*263*/264static inline void smack_str_from_perm(char *string, int access)265{266int i = 0;267if (access & MAY_READ)268string[i++] = 'r';269if (access & MAY_WRITE)270string[i++] = 'w';271if (access & MAY_EXEC)272string[i++] = 'x';273if (access & MAY_APPEND)274string[i++] = 'a';275string[i] = '\0';276}277/**278* smack_log_callback - SMACK specific information279* will be called by generic audit code280* @ab : the audit_buffer281* @a : audit_data282*283*/284static void smack_log_callback(struct audit_buffer *ab, void *a)285{286struct common_audit_data *ad = a;287struct smack_audit_data *sad = &ad->smack_audit_data;288audit_log_format(ab, "lsm=SMACK fn=%s action=%s",289ad->smack_audit_data.function,290sad->result ? "denied" : "granted");291audit_log_format(ab, " subject=");292audit_log_untrustedstring(ab, sad->subject);293audit_log_format(ab, " object=");294audit_log_untrustedstring(ab, sad->object);295audit_log_format(ab, " requested=%s", sad->request);296}297298/**299* smack_log - Audit the granting or denial of permissions.300* @subject_label : smack label of the requester301* @object_label : smack label of the object being accessed302* @request: requested permissions303* @result: result from smk_access304* @a: auxiliary audit data305*306* Audit the granting or denial of permissions in accordance307* with the policy.308*/309void smack_log(char *subject_label, char *object_label, int request,310int result, struct smk_audit_info *ad)311{312char request_buffer[SMK_NUM_ACCESS_TYPE + 1];313struct smack_audit_data *sad;314struct common_audit_data *a = &ad->a;315316/* check if we have to log the current event */317if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)318return;319if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)320return;321322if (a->smack_audit_data.function == NULL)323a->smack_audit_data.function = "unknown";324325/* end preparing the audit data */326sad = &a->smack_audit_data;327smack_str_from_perm(request_buffer, request);328sad->subject = subject_label;329sad->object = object_label;330sad->request = request_buffer;331sad->result = result;332a->lsm_pre_audit = smack_log_callback;333334common_lsm_audit(a);335}336#else /* #ifdef CONFIG_AUDIT */337void smack_log(char *subject_label, char *object_label, int request,338int result, struct smk_audit_info *ad)339{340}341#endif342343static DEFINE_MUTEX(smack_known_lock);344345/**346* smk_import_entry - import a label, return the list entry347* @string: a text string that might be a Smack label348* @len: the maximum size, or zero if it is NULL terminated.349*350* Returns a pointer to the entry in the label list that351* matches the passed string, adding it if necessary.352*/353struct smack_known *smk_import_entry(const char *string, int len)354{355struct smack_known *skp;356char smack[SMK_LABELLEN];357int found;358int i;359360if (len <= 0 || len > SMK_MAXLEN)361len = SMK_MAXLEN;362363for (i = 0, found = 0; i < SMK_LABELLEN; i++) {364if (found)365smack[i] = '\0';366else if (i >= len || string[i] > '~' || string[i] <= ' ' ||367string[i] == '/' || string[i] == '"' ||368string[i] == '\\' || string[i] == '\'') {369smack[i] = '\0';370found = 1;371} else372smack[i] = string[i];373}374375if (smack[0] == '\0')376return NULL;377378mutex_lock(&smack_known_lock);379380found = 0;381list_for_each_entry_rcu(skp, &smack_known_list, list) {382if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {383found = 1;384break;385}386}387388if (found == 0) {389skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);390if (skp != NULL) {391strncpy(skp->smk_known, smack, SMK_MAXLEN);392skp->smk_secid = smack_next_secid++;393skp->smk_cipso = NULL;394spin_lock_init(&skp->smk_cipsolock);395/*396* Make sure that the entry is actually397* filled before putting it on the list.398*/399list_add_rcu(&skp->list, &smack_known_list);400}401}402403mutex_unlock(&smack_known_lock);404405return skp;406}407408/**409* smk_import - import a smack label410* @string: a text string that might be a Smack label411* @len: the maximum size, or zero if it is NULL terminated.412*413* Returns a pointer to the label in the label list that414* matches the passed string, adding it if necessary.415*/416char *smk_import(const char *string, int len)417{418struct smack_known *skp;419420/* labels cannot begin with a '-' */421if (string[0] == '-')422return NULL;423skp = smk_import_entry(string, len);424if (skp == NULL)425return NULL;426return skp->smk_known;427}428429/**430* smack_from_secid - find the Smack label associated with a secid431* @secid: an integer that might be associated with a Smack label432*433* Returns a pointer to the appropriate Smack label if there is one,434* otherwise a pointer to the invalid Smack label.435*/436char *smack_from_secid(const u32 secid)437{438struct smack_known *skp;439440rcu_read_lock();441list_for_each_entry_rcu(skp, &smack_known_list, list) {442if (skp->smk_secid == secid) {443rcu_read_unlock();444return skp->smk_known;445}446}447448/*449* If we got this far someone asked for the translation450* of a secid that is not on the list.451*/452rcu_read_unlock();453return smack_known_invalid.smk_known;454}455456/**457* smack_to_secid - find the secid associated with a Smack label458* @smack: the Smack label459*460* Returns the appropriate secid if there is one,461* otherwise 0462*/463u32 smack_to_secid(const char *smack)464{465struct smack_known *skp;466467rcu_read_lock();468list_for_each_entry_rcu(skp, &smack_known_list, list) {469if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {470rcu_read_unlock();471return skp->smk_secid;472}473}474rcu_read_unlock();475return 0;476}477478/**479* smack_from_cipso - find the Smack label associated with a CIPSO option480* @level: Bell & LaPadula level from the network481* @cp: Bell & LaPadula categories from the network482* @result: where to put the Smack value483*484* This is a simple lookup in the label table.485*486* This is an odd duck as far as smack handling goes in that487* it sends back a copy of the smack label rather than a pointer488* to the master list. This is done because it is possible for489* a foreign host to send a smack label that is new to this490* machine and hence not on the list. That would not be an491* issue except that adding an entry to the master list can't492* be done at that point.493*/494void smack_from_cipso(u32 level, char *cp, char *result)495{496struct smack_known *kp;497char *final = NULL;498499rcu_read_lock();500list_for_each_entry(kp, &smack_known_list, list) {501if (kp->smk_cipso == NULL)502continue;503504spin_lock_bh(&kp->smk_cipsolock);505506if (kp->smk_cipso->smk_level == level &&507memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)508final = kp->smk_known;509510spin_unlock_bh(&kp->smk_cipsolock);511}512rcu_read_unlock();513if (final == NULL)514final = smack_known_huh.smk_known;515strncpy(result, final, SMK_MAXLEN);516return;517}518519/**520* smack_to_cipso - find the CIPSO option to go with a Smack label521* @smack: a pointer to the smack label in question522* @cp: where to put the result523*524* Returns zero if a value is available, non-zero otherwise.525*/526int smack_to_cipso(const char *smack, struct smack_cipso *cp)527{528struct smack_known *kp;529int found = 0;530531rcu_read_lock();532list_for_each_entry_rcu(kp, &smack_known_list, list) {533if (kp->smk_known == smack ||534strcmp(kp->smk_known, smack) == 0) {535found = 1;536break;537}538}539rcu_read_unlock();540541if (found == 0 || kp->smk_cipso == NULL)542return -ENOENT;543544memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));545return 0;546}547548549