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/slab.h>
  15#include <linux/fs.h>
  16#include <linux/sched.h>
  17#include "smack.h"
  18
  19struct smack_known smack_known_huh = {
  20        .smk_known      = "?",
  21        .smk_secid      = 2,
  22        .smk_cipso      = NULL,
  23};
  24
  25struct smack_known smack_known_hat = {
  26        .smk_known      = "^",
  27        .smk_secid      = 3,
  28        .smk_cipso      = NULL,
  29};
  30
  31struct smack_known smack_known_star = {
  32        .smk_known      = "*",
  33        .smk_secid      = 4,
  34        .smk_cipso      = NULL,
  35};
  36
  37struct smack_known smack_known_floor = {
  38        .smk_known      = "_",
  39        .smk_secid      = 5,
  40        .smk_cipso      = NULL,
  41};
  42
  43struct smack_known smack_known_invalid = {
  44        .smk_known      = "",
  45        .smk_secid      = 6,
  46        .smk_cipso      = NULL,
  47};
  48
  49struct smack_known smack_known_web = {
  50        .smk_known      = "@",
  51        .smk_secid      = 7,
  52        .smk_cipso      = NULL,
  53};
  54
  55LIST_HEAD(smack_known_list);
  56
  57/*
  58 * The initial value needs to be bigger than any of the
  59 * known values above.
  60 */
  61static u32 smack_next_secid = 10;
  62
  63/*
  64 * what events do we log
  65 * can be overwritten at run-time by /smack/logging
  66 */
  67int log_policy = SMACK_AUDIT_DENIED;
  68
  69/**
  70 * smk_access - determine if a subject has a specific access to an object
  71 * @subject_label: a pointer to the subject's Smack label
  72 * @object_label: a pointer to the object's Smack label
  73 * @request: the access requested, in "MAY" format
  74 * @a : a pointer to the audit data
  75 *
  76 * This function looks up the subject/object pair in the
  77 * access rule list and returns 0 if the access is permitted,
  78 * non zero otherwise.
  79 *
  80 * Even though Smack labels are usually shared on smack_list
  81 * labels that come in off the network can't be imported
  82 * and added to the list for locking reasons.
  83 *
  84 * Therefore, it is necessary to check the contents of the labels,
  85 * not just the pointer values. Of course, in most cases the labels
  86 * will be on the list, so checking the pointers may be a worthwhile
  87 * optimization.
  88 */
  89int smk_access(char *subject_label, char *object_label, int request,
  90               struct smk_audit_info *a)
  91{
  92        u32 may = MAY_NOT;
  93        struct smack_rule *srp;
  94        int rc = 0;
  95
  96        /*
  97         * Hardcoded comparisons.
  98         *
  99         * A star subject can't access any object.
 100         */
 101        if (subject_label == smack_known_star.smk_known ||
 102            strcmp(subject_label, smack_known_star.smk_known) == 0) {
 103                rc = -EACCES;
 104                goto out_audit;
 105        }
 106        /*
 107         * An internet object can be accessed by any subject.
 108         * Tasks cannot be assigned the internet label.
 109         * An internet subject can access any object.
 110         */
 111        if (object_label == smack_known_web.smk_known ||
 112            subject_label == smack_known_web.smk_known ||
 113            strcmp(object_label, smack_known_web.smk_known) == 0 ||
 114            strcmp(subject_label, smack_known_web.smk_known) == 0)
 115                goto out_audit;
 116        /*
 117         * A star object can be accessed by any subject.
 118         */
 119        if (object_label == smack_known_star.smk_known ||
 120            strcmp(object_label, smack_known_star.smk_known) == 0)
 121                goto out_audit;
 122        /*
 123         * An object can be accessed in any way by a subject
 124         * with the same label.
 125         */
 126        if (subject_label == object_label ||
 127            strcmp(subject_label, object_label) == 0)
 128                goto out_audit;
 129        /*
 130         * A hat subject can read any object.
 131         * A floor object can be read by any subject.
 132         */
 133        if ((request & MAY_ANYREAD) == request) {
 134                if (object_label == smack_known_floor.smk_known ||
 135                    strcmp(object_label, smack_known_floor.smk_known) == 0)
 136                        goto out_audit;
 137                if (subject_label == smack_known_hat.smk_known ||
 138                    strcmp(subject_label, smack_known_hat.smk_known) == 0)
 139                        goto out_audit;
 140        }
 141        /*
 142         * Beyond here an explicit relationship is required.
 143         * If the requested access is contained in the available
 144         * access (e.g. read is included in readwrite) it's
 145         * good.
 146         */
 147        rcu_read_lock();
 148        list_for_each_entry_rcu(srp, &smack_rule_list, list) {
 149                if (srp->smk_subject == subject_label ||
 150                    strcmp(srp->smk_subject, subject_label) == 0) {
 151                        if (srp->smk_object == object_label ||
 152                            strcmp(srp->smk_object, object_label) == 0) {
 153                                may = srp->smk_access;
 154                                break;
 155                        }
 156                }
 157        }
 158        rcu_read_unlock();
 159        /*
 160         * This is a bit map operation.
 161         */
 162        if ((request & may) == request)
 163                goto out_audit;
 164
 165        rc = -EACCES;
 166out_audit:
 167#ifdef CONFIG_AUDIT
 168        if (a)
 169                smack_log(subject_label, object_label, request, rc, a);
 170#endif
 171        return rc;
 172}
 173
 174/**
 175 * smk_curacc - determine if current has a specific access to an object
 176 * @obj_label: a pointer to the object's Smack label
 177 * @mode: the access requested, in "MAY" format
 178 * @a : common audit data
 179 *
 180 * This function checks the current subject label/object label pair
 181 * in the access rule list and returns 0 if the access is permitted,
 182 * non zero otherwise. It allows that current may have the capability
 183 * to override the rules.
 184 */
 185int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
 186{
 187        int rc;
 188        char *sp = current_security();
 189
 190        rc = smk_access(sp, obj_label, mode, NULL);
 191        if (rc == 0)
 192                goto out_audit;
 193
 194        /*
 195         * Return if a specific label has been designated as the
 196         * only one that gets privilege and current does not
 197         * have that label.
 198         */
 199        if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
 200                goto out_audit;
 201
 202        if (capable(CAP_MAC_OVERRIDE))
 203                return 0;
 204
 205out_audit:
 206#ifdef CONFIG_AUDIT
 207        if (a)
 208                smack_log(sp, obj_label, mode, rc, a);
 209#endif
 210        return rc;
 211}
 212
 213#ifdef CONFIG_AUDIT
 214/**
 215 * smack_str_from_perm : helper to transalate an int to a
 216 * readable string
 217 * @string : the string to fill
 218 * @access : the int
 219 *
 220 */
 221static inline void smack_str_from_perm(char *string, int access)
 222{
 223        int i = 0;
 224        if (access & MAY_READ)
 225                string[i++] = 'r';
 226        if (access & MAY_WRITE)
 227                string[i++] = 'w';
 228        if (access & MAY_EXEC)
 229                string[i++] = 'x';
 230        if (access & MAY_APPEND)
 231                string[i++] = 'a';
 232        string[i] = '\0';
 233}
 234/**
 235 * smack_log_callback - SMACK specific information
 236 * will be called by generic audit code
 237 * @ab : the audit_buffer
 238 * @a  : audit_data
 239 *
 240 */
 241static void smack_log_callback(struct audit_buffer *ab, void *a)
 242{
 243        struct common_audit_data *ad = a;
 244        struct smack_audit_data *sad = &ad->smack_audit_data;
 245        audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
 246                         ad->smack_audit_data.function,
 247                         sad->result ? "denied" : "granted");
 248        audit_log_format(ab, " subject=");
 249        audit_log_untrustedstring(ab, sad->subject);
 250        audit_log_format(ab, " object=");
 251        audit_log_untrustedstring(ab, sad->object);
 252        audit_log_format(ab, " requested=%s", sad->request);
 253}
 254
 255/**
 256 *  smack_log - Audit the granting or denial of permissions.
 257 *  @subject_label : smack label of the requester
 258 *  @object_label  : smack label of the object being accessed
 259 *  @request: requested permissions
 260 *  @result: result from smk_access
 261 *  @a:  auxiliary audit data
 262 *
 263 * Audit the granting or denial of permissions in accordance
 264 * with the policy.
 265 */
 266void smack_log(char *subject_label, char *object_label, int request,
 267               int result, struct smk_audit_info *ad)
 268{
 269        char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
 270        struct smack_audit_data *sad;
 271        struct common_audit_data *a = &ad->a;
 272
 273        /* check if we have to log the current event */
 274        if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
 275                return;
 276        if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
 277                return;
 278
 279        if (a->smack_audit_data.function == NULL)
 280                a->smack_audit_data.function = "unknown";
 281
 282        /* end preparing the audit data */
 283        sad = &a->smack_audit_data;
 284        smack_str_from_perm(request_buffer, request);
 285        sad->subject = subject_label;
 286        sad->object  = object_label;
 287        sad->request = request_buffer;
 288        sad->result  = result;
 289        a->lsm_pre_audit = smack_log_callback;
 290
 291        common_lsm_audit(a);
 292}
 293#else /* #ifdef CONFIG_AUDIT */
 294void smack_log(char *subject_label, char *object_label, int request,
 295               int result, struct smk_audit_info *ad)
 296{
 297}
 298#endif
 299
 300static DEFINE_MUTEX(smack_known_lock);
 301
 302/**
 303 * smk_import_entry - import a label, return the list entry
 304 * @string: a text string that might be a Smack label
 305 * @len: the maximum size, or zero if it is NULL terminated.
 306 *
 307 * Returns a pointer to the entry in the label list that
 308 * matches the passed string, adding it if necessary.
 309 */
 310struct smack_known *smk_import_entry(const char *string, int len)
 311{
 312        struct smack_known *skp;
 313        char smack[SMK_LABELLEN];
 314        int found;
 315        int i;
 316
 317        if (len <= 0 || len > SMK_MAXLEN)
 318                len = SMK_MAXLEN;
 319
 320        for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
 321                if (found)
 322                        smack[i] = '\0';
 323                else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
 324                         string[i] == '/' || string[i] == '"' ||
 325                         string[i] == '\\' || string[i] == '\'') {
 326                        smack[i] = '\0';
 327                        found = 1;
 328                } else
 329                        smack[i] = string[i];
 330        }
 331
 332        if (smack[0] == '\0')
 333                return NULL;
 334
 335        mutex_lock(&smack_known_lock);
 336
 337        found = 0;
 338        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 339                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
 340                        found = 1;
 341                        break;
 342                }
 343        }
 344
 345        if (found == 0) {
 346                skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 347                if (skp != NULL) {
 348                        strncpy(skp->smk_known, smack, SMK_MAXLEN);
 349                        skp->smk_secid = smack_next_secid++;
 350                        skp->smk_cipso = NULL;
 351                        spin_lock_init(&skp->smk_cipsolock);
 352                        /*
 353                         * Make sure that the entry is actually
 354                         * filled before putting it on the list.
 355                         */
 356                        list_add_rcu(&skp->list, &smack_known_list);
 357                }
 358        }
 359
 360        mutex_unlock(&smack_known_lock);
 361
 362        return skp;
 363}
 364
 365/**
 366 * smk_import - import a smack label
 367 * @string: a text string that might be a Smack label
 368 * @len: the maximum size, or zero if it is NULL terminated.
 369 *
 370 * Returns a pointer to the label in the label list that
 371 * matches the passed string, adding it if necessary.
 372 */
 373char *smk_import(const char *string, int len)
 374{
 375        struct smack_known *skp;
 376
 377        /* labels cannot begin with a '-' */
 378        if (string[0] == '-')
 379                return NULL;
 380        skp = smk_import_entry(string, len);
 381        if (skp == NULL)
 382                return NULL;
 383        return skp->smk_known;
 384}
 385
 386/**
 387 * smack_from_secid - find the Smack label associated with a secid
 388 * @secid: an integer that might be associated with a Smack label
 389 *
 390 * Returns a pointer to the appropraite Smack label if there is one,
 391 * otherwise a pointer to the invalid Smack label.
 392 */
 393char *smack_from_secid(const u32 secid)
 394{
 395        struct smack_known *skp;
 396
 397        rcu_read_lock();
 398        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 399                if (skp->smk_secid == secid) {
 400                        rcu_read_unlock();
 401                        return skp->smk_known;
 402                }
 403        }
 404
 405        /*
 406         * If we got this far someone asked for the translation
 407         * of a secid that is not on the list.
 408         */
 409        rcu_read_unlock();
 410        return smack_known_invalid.smk_known;
 411}
 412
 413/**
 414 * smack_to_secid - find the secid associated with a Smack label
 415 * @smack: the Smack label
 416 *
 417 * Returns the appropriate secid if there is one,
 418 * otherwise 0
 419 */
 420u32 smack_to_secid(const char *smack)
 421{
 422        struct smack_known *skp;
 423
 424        rcu_read_lock();
 425        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 426                if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
 427                        rcu_read_unlock();
 428                        return skp->smk_secid;
 429                }
 430        }
 431        rcu_read_unlock();
 432        return 0;
 433}
 434
 435/**
 436 * smack_from_cipso - find the Smack label associated with a CIPSO option
 437 * @level: Bell & LaPadula level from the network
 438 * @cp: Bell & LaPadula categories from the network
 439 * @result: where to put the Smack value
 440 *
 441 * This is a simple lookup in the label table.
 442 *
 443 * This is an odd duck as far as smack handling goes in that
 444 * it sends back a copy of the smack label rather than a pointer
 445 * to the master list. This is done because it is possible for
 446 * a foreign host to send a smack label that is new to this
 447 * machine and hence not on the list. That would not be an
 448 * issue except that adding an entry to the master list can't
 449 * be done at that point.
 450 */
 451void smack_from_cipso(u32 level, char *cp, char *result)
 452{
 453        struct smack_known *kp;
 454        char *final = NULL;
 455
 456        rcu_read_lock();
 457        list_for_each_entry(kp, &smack_known_list, list) {
 458                if (kp->smk_cipso == NULL)
 459                        continue;
 460
 461                spin_lock_bh(&kp->smk_cipsolock);
 462
 463                if (kp->smk_cipso->smk_level == level &&
 464                    memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
 465                        final = kp->smk_known;
 466
 467                spin_unlock_bh(&kp->smk_cipsolock);
 468        }
 469        rcu_read_unlock();
 470        if (final == NULL)
 471                final = smack_known_huh.smk_known;
 472        strncpy(result, final, SMK_MAXLEN);
 473        return;
 474}
 475
 476/**
 477 * smack_to_cipso - find the CIPSO option to go with a Smack label
 478 * @smack: a pointer to the smack label in question
 479 * @cp: where to put the result
 480 *
 481 * Returns zero if a value is available, non-zero otherwise.
 482 */
 483int smack_to_cipso(const char *smack, struct smack_cipso *cp)
 484{
 485        struct smack_known *kp;
 486        int found = 0;
 487
 488        rcu_read_lock();
 489        list_for_each_entry_rcu(kp, &smack_known_list, list) {
 490                if (kp->smk_known == smack ||
 491                    strcmp(kp->smk_known, smack) == 0) {
 492                        found = 1;
 493                        break;
 494                }
 495        }
 496        rcu_read_unlock();
 497
 498        if (found == 0 || kp->smk_cipso == NULL)
 499                return -ENOENT;
 500
 501        memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
 502        return 0;
 503}
 504
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.