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 may 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        /*
 172         * Return if a specific label has been designated as the
 173         * only one that gets privilege and current does not
 174         * have that label.
 175         */
 176        if (smack_onlycap != NULL && smack_onlycap != current->security)
 177                return rc;
 178
 179        if (capable(CAP_MAC_OVERRIDE))
 180                return 0;
 181
 182        return rc;
 183}
 184
 185static DEFINE_MUTEX(smack_known_lock);
 186
 187/**
 188 * smk_import_entry - import a label, return the list entry
 189 * @string: a text string that might be a Smack label
 190 * @len: the maximum size, or zero if it is NULL terminated.
 191 *
 192 * Returns a pointer to the entry in the label list that
 193 * matches the passed string, adding it if necessary.
 194 */
 195struct smack_known *smk_import_entry(const char *string, int len)
 196{
 197        struct smack_known *skp;
 198        char smack[SMK_LABELLEN];
 199        int found;
 200        int i;
 201
 202        if (len <= 0 || len > SMK_MAXLEN)
 203                len = SMK_MAXLEN;
 204
 205        for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
 206                if (found)
 207                        smack[i] = '\0';
 208                else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
 209                         string[i] == '/') {
 210                        smack[i] = '\0';
 211                        found = 1;
 212                } else
 213                        smack[i] = string[i];
 214        }
 215
 216        if (smack[0] == '\0')
 217                return NULL;
 218
 219        mutex_lock(&smack_known_lock);
 220
 221        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 222                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
 223                        break;
 224
 225        if (skp == NULL) {
 226                skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 227                if (skp != NULL) {
 228                        skp->smk_next = smack_known;
 229                        strncpy(skp->smk_known, smack, SMK_MAXLEN);
 230                        skp->smk_secid = smack_next_secid++;
 231                        skp->smk_cipso = NULL;
 232                        spin_lock_init(&skp->smk_cipsolock);
 233                        /*
 234                         * Make sure that the entry is actually
 235                         * filled before putting it on the list.
 236                         */
 237                        smp_mb();
 238                        smack_known = skp;
 239                }
 240        }
 241
 242        mutex_unlock(&smack_known_lock);
 243
 244        return skp;
 245}
 246
 247/**
 248 * smk_import - import a smack label
 249 * @string: a text string that might be a Smack label
 250 * @len: the maximum size, or zero if it is NULL terminated.
 251 *
 252 * Returns a pointer to the label in the label list that
 253 * matches the passed string, adding it if necessary.
 254 */
 255char *smk_import(const char *string, int len)
 256{
 257        struct smack_known *skp;
 258
 259        skp = smk_import_entry(string, len);
 260        if (skp == NULL)
 261                return NULL;
 262        return skp->smk_known;
 263}
 264
 265/**
 266 * smack_from_secid - find the Smack label associated with a secid
 267 * @secid: an integer that might be associated with a Smack label
 268 *
 269 * Returns a pointer to the appropraite Smack label if there is one,
 270 * otherwise a pointer to the invalid Smack label.
 271 */
 272char *smack_from_secid(const u32 secid)
 273{
 274        struct smack_known *skp;
 275
 276        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 277                if (skp->smk_secid == secid)
 278                        return skp->smk_known;
 279
 280        /*
 281         * If we got this far someone asked for the translation
 282         * of a secid that is not on the list.
 283         */
 284        return smack_known_invalid.smk_known;
 285}
 286
 287/**
 288 * smack_to_secid - find the secid associated with a Smack label
 289 * @smack: the Smack label
 290 *
 291 * Returns the appropriate secid if there is one,
 292 * otherwise 0
 293 */
 294u32 smack_to_secid(const char *smack)
 295{
 296        struct smack_known *skp;
 297
 298        for (skp = smack_known; skp != NULL; skp = skp->smk_next)
 299                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
 300                        return skp->smk_secid;
 301        return 0;
 302}
 303
 304/**
 305 * smack_from_cipso - find the Smack label associated with a CIPSO option
 306 * @level: Bell & LaPadula level from the network
 307 * @cp: Bell & LaPadula categories from the network
 308 * @result: where to put the Smack value
 309 *
 310 * This is a simple lookup in the label table.
 311 *
 312 * This is an odd duck as far as smack handling goes in that
 313 * it sends back a copy of the smack label rather than a pointer
 314 * to the master list. This is done because it is possible for
 315 * a foreign host to send a smack label that is new to this
 316 * machine and hence not on the list. That would not be an
 317 * issue except that adding an entry to the master list can't
 318 * be done at that point.
 319 */
 320void smack_from_cipso(u32 level, char *cp, char *result)
 321{
 322        struct smack_known *kp;
 323        char *final = NULL;
 324
 325        for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) {
 326                if (kp->smk_cipso == NULL)
 327                        continue;
 328
 329                spin_lock_bh(&kp->smk_cipsolock);
 330
 331                if (kp->smk_cipso->smk_level == level &&
 332                    memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
 333                        final = kp->smk_known;
 334
 335                spin_unlock_bh(&kp->smk_cipsolock);
 336        }
 337        if (final == NULL)
 338                final = smack_known_huh.smk_known;
 339        strncpy(result, final, SMK_MAXLEN);
 340        return;
 341}
 342
 343/**
 344 * smack_to_cipso - find the CIPSO option to go with a Smack label
 345 * @smack: a pointer to the smack label in question
 346 * @cp: where to put the result
 347 *
 348 * Returns zero if a value is available, non-zero otherwise.
 349 */
 350int smack_to_cipso(const char *smack, struct smack_cipso *cp)
 351{
 352        struct smack_known *kp;
 353
 354        for (kp = smack_known; kp != NULL; kp = kp->smk_next)
 355                if (kp->smk_known == smack ||
 356                    strcmp(kp->smk_known, smack) == 0)
 357                        break;
 358
 359        if (kp == NULL || kp->smk_cipso == NULL)
 360                return -ENOENT;
 361
 362        memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
 363        return 0;
 364}
 365