linux/security/smack/smack_access.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
   3 *
   4 *      This program is free software; you can redistribute it and/or modify
   5 *      it under the terms of the GNU General Public License as published by
   6 *      the Free Software Foundation, version 2.
   7 *
   8 * Author:
   9 *      Casey Schaufler <casey@schaufler-ca.com>
  10 *
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/fs.h>
  15#include <linux/sched.h>
  16#include "smack.h"
  17
  18struct smack_known smack_known_unset = {
  19        .smk_next       = NULL,
  20        .smk_known      = "UNSET",
  21        .smk_secid      = 1,
  22        .smk_cipso      = NULL,
  23};
  24
  25struct smack_known smack_known_huh = {
  26        .smk_next       = &smack_known_unset,
  27        .smk_known      = "?",
  28        .smk_secid      = 2,
  29        .smk_cipso      = NULL,
  30};
  31
  32struct smack_known smack_known_hat = {
  33        .smk_next       = &smack_known_huh,
  34        .smk_known      = "^",
  35        .smk_secid      = 3,
  36        .smk_cipso      = NULL,
  37};
  38
  39struct smack_known smack_known_star = {
  40        .smk_next       = &smack_known_hat,
  41        .smk_known      = "*",
  42        .smk_secid      = 4,
  43        .smk_cipso      = NULL,
  44};
  45
  46struct smack_known smack_known_floor = {
  47        .smk_next       = &smack_known_star,
  48        .smk_known      = "_",
  49        .smk_secid      = 5,
  50        .smk_cipso      = NULL,
  51};
  52
  53struct smack_known smack_known_invalid = {
  54        .smk_next       = &smack_known_floor,
  55        .smk_known      = "",
  56        .smk_secid      = 6,
  57        .smk_cipso      = NULL,
  58};
  59
  60struct smack_known *smack_known = &smack_known_invalid;
  61
  62/*
  63 * The initial value needs to be bigger than any of the
  64 * known values above.
  65 */
  66static u32 smack_next_secid = 10;
  67
  68/**
  69 * smk_access - determine if a subject has a specific access to an object
  70 * @subject_label: a pointer to the subject's Smack label
  71 * @object_label: a pointer to the object's Smack label
  72 * @request: the access requested, in "MAY" format
  73 *
  74 * This function looks up the subject/object pair in the
  75 * access rule list and returns 0 if the access is permitted,
  76 * non zero otherwise.
  77 *
  78 * Even though Smack labels are usually shared on smack_list
  79 * labels that come in off the network can't be imported
  80 * and added to the list for locking reasons.
  81 *
  82 * Therefore, it is necessary to check the contents of the labels,
  83 * not just the pointer values. Of course, in most cases the labels
  84 * will be on the list, so checking the pointers may be a worthwhile
  85 * optimization.
  86 */
  87int smk_access(char *subject_label, char *object_label, int request)
  88{
  89        u32 may = MAY_NOT;
  90        struct smk_list_entry *sp;
  91        struct smack_rule *srp;
  92
  93        /*
  94         * Hardcoded comparisons.
  95         *
  96         * A star subject can't access any object.
  97         */
  98        if (subject_label == smack_known_star.smk_known ||
  99            strcmp(subject_label, smack_known_star.smk_known) == 0)
 100                return -EACCES;
 101        /*
 102         * A star object can be accessed by any subject.
 103         */
 104        if (object_label == smack_known_star.smk_known ||
 105            strcmp(object_label, smack_known_star.smk_known) == 0)
 106                return 0;
 107        /*
 108         * An object can be accessed in any way by a subject
 109         * with the same label.
 110         */
 111        if (subject_label == object_label ||
 112            strcmp(subject_label, object_label) == 0)
 113                return 0;
 114        /*
 115         * A hat subject can read any object.
 116         * A floor object can be read by any subject.
 117         */
 118        if ((request & MAY_ANYREAD) == request) {
 119                if (object_label == smack_known_floor.smk_known ||
 120                    strcmp(object_label, smack_known_floor.smk_known) == 0)
 121                        return 0;
 122                if (subject_label == smack_known_hat.smk_known ||
 123                    strcmp(subject_label, smack_known_hat.smk_known) == 0)
 124                        return 0;
 125        }
 126        /*
 127         * Beyond here an explicit relationship is required.
 128         * If the requested access is contained in the available
 129         * access (e.g. read is included in readwrite) it's
 130         * good.
 131         */
 132        for (sp = smack_list; sp != NULL; sp = sp->smk_next) {
 133                srp = &sp->smk_rule;
 134
 135                if (srp->smk_subject == subject_label ||
 136                    strcmp(srp->smk_subject, subject_label) == 0) {
 137                        if (srp->smk_object == object_label ||
 138                            strcmp(srp->smk_object, object_label) == 0) {
 139                                may = srp->smk_access;
 140                                break;
 141                        }
 142                }
 143        }
 144        /*
 145         * This is a bit map operation.
 146         */
 147        if ((request & may) == request)
 148                return 0;
 149
 150        return -EACCES;
 151}
 152
 153/**
 154 * smk_curacc - determine if current has a specific access to an object
 155 * @object_label: a pointer to the object's Smack label
 156 * @request: the access requested, in "MAY" format
 157 *
 158 * This function checks the current subject label/object label pair
 159 * in the access rule list and returns 0 if the access is permitted,
 160 * non zero otherwise. It allows that current my have the capability
 161 * to override the rules.
 162 */
 163int smk_curacc(char *obj_label, u32 mode)
 164{
 165        int rc;
 166
 167        rc = smk_access(current->security, obj_label, mode);
 168        if (rc == 0)
 169                return 0;
 170
 171        if (capable(CAP_MAC_OVERRIDE))
 172                return 0;
 173
 174        return rc;
 175}
 176
 177static DEFINE_MUTEX(smack_known_lock);
 178
 179/**
 180 * smk_import_entry - import a label, return the list entry
 181 * @string: a text string that might be a Smack label
 182 * @len: the maximum size, or zero if it is NULL terminated.
 183 *
 184 * Returns a pointer to the entry in the label list that
 185 * matches the passed string, adding it if necessary.
 186 */
 187struct smack_known *smk_import_entry(const char *string, int len)
 188{
 189        struct smack_known *skp;
 190        char smack[SMK_LABELLEN];
 191        int found;
 192        int i;
 193
 194        if (len <= 0 || len > SMK_MAXLEN)
 195                len = SMK_MAXLEN;
 196
 197        for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
 198                if (found)
 199                        smack[i] = '\0';
 200                else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
 201                         string[i] == '/') {
 202                        smack[i] = '\0';
 203                        found = 1;
 204                } else
 205                        smack[i] = string[i];
 206        }
 207
 208        if (smack[0] == '\0')
 209                return NULL;
 210
 211        mutex_lock(&smack_known_lock);
 212
 213        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 214                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
 215                        break;
 216
 217        if (skp == NULL) {
 218                skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 219                if (skp != NULL) {
 220                        skp->smk_next = smack_known;
 221                        strncpy(skp->smk_known, smack, SMK_MAXLEN);
 222                        skp->smk_secid = smack_next_secid++;
 223                        skp->smk_cipso = NULL;
 224                        spin_lock_init(&skp->smk_cipsolock);
 225                        /*
 226                         * Make sure that the entry is actually
 227                         * filled before putting it on the list.
 228                         */
 229                        smp_mb();
 230                        smack_known = skp;
 231                }
 232        }
 233
 234        mutex_unlock(&smack_known_lock);
 235
 236        return skp;
 237}
 238
 239/**
 240 * smk_import - import a smack label
 241 * @string: a text string that might be a Smack label
 242 * @len: the maximum size, or zero if it is NULL terminated.
 243 *
 244 * Returns a pointer to the label in the label list that
 245 * matches the passed string, adding it if necessary.
 246 */
 247char *smk_import(const char *string, int len)
 248{
 249        struct smack_known *skp;
 250
 251        skp = smk_import_entry(string, len);
 252        if (skp == NULL)
 253                return NULL;
 254        return skp->smk_known;
 255}
 256
 257/**
 258 * smack_from_secid - find the Smack label associated with a secid
 259 * @secid: an integer that might be associated with a Smack label
 260 *
 261 * Returns a pointer to the appropraite Smack label if there is one,
 262 * otherwise a pointer to the invalid Smack label.
 263 */
 264char *smack_from_secid(const u32 secid)
 265{
 266        struct smack_known *skp;
 267
 268        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 269                if (skp->smk_secid == secid)
 270                        return skp->smk_known;
 271
 272        /*
 273         * If we got this far someone asked for the translation
 274         * of a secid that is not on the list.
 275         */
 276        return smack_known_invalid.smk_known;
 277}
 278
 279/**
 280 * smack_to_secid - find the secid associated with a Smack label
 281 * @smack: the Smack label
 282 *
 283 * Returns the appropriate secid if there is one,
 284 * otherwise 0
 285 */
 286u32 smack_to_secid(const char *smack)
 287{
 288        struct smack_known *skp;
 289
 290        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 291                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
 292                        return skp->smk_secid;
 293        return 0;
 294}
 295
 296/**
 297 * smack_from_cipso - find the Smack label associated with a CIPSO option
 298 * @level: Bell & LaPadula level from the network
 299 * @cp: Bell & LaPadula categories from the network
 300 * @result: where to put the Smack value
 301 *
 302 * This is a simple lookup in the label table.
 303 *
 304 * This is an odd duck as far as smack handling goes in that
 305 * it sends back a copy of the smack label rather than a pointer
 306 * to the master list. This is done because it is possible for
 307 * a foreign host to send a smack label that is new to this
 308 * machine and hence not on the list. That would not be an
 309 * issue except that adding an entry to the master list can't
 310 * be done at that point.
 311 */
 312void smack_from_cipso(u32 level, char *cp, char *result)
 313{
 314        struct smack_known *kp;
 315        char *final = NULL;
 316
 317        for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) {
 318                if (kp->smk_cipso == NULL)
 319                        continue;
 320
 321                spin_lock_bh(&kp->smk_cipsolock);
 322
 323                if (kp->smk_cipso->smk_level == level &&
 324                    memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
 325                        final = kp->smk_known;
 326
 327                spin_unlock_bh(&kp->smk_cipsolock);
 328        }
 329        if (final == NULL)
 330                final = smack_known_huh.smk_known;
 331        strncpy(result, final, SMK_MAXLEN);
 332        return;
 333}
 334
 335/**
 336 * smack_to_cipso - find the CIPSO option to go with a Smack label
 337 * @smack: a pointer to the smack label in question
 338 * @cp: where to put the result
 339 *
 340 * Returns zero if a value is available, non-zero otherwise.
 341 */
 342int smack_to_cipso(const char *smack, struct smack_cipso *cp)
 343{
 344        struct smack_known *kp;
 345
 346        for (kp = smack_known; kp != NULL; kp = kp->smk_next)
 347                if (kp->smk_known == smack ||
 348                    strcmp(kp->smk_known, smack) == 0)
 349                        break;
 350
 351        if (kp == NULL || kp->smk_cipso == NULL)
 352                return -ENOENT;
 353
 354        memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
 355        return 0;
 356}
 357