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};
  23
  24struct smack_known smack_known_hat = {
  25        .smk_known      = "^",
  26        .smk_secid      = 3,
  27};
  28
  29struct smack_known smack_known_star = {
  30        .smk_known      = "*",
  31        .smk_secid      = 4,
  32};
  33
  34struct smack_known smack_known_floor = {
  35        .smk_known      = "_",
  36        .smk_secid      = 5,
  37};
  38
  39struct smack_known smack_known_invalid = {
  40        .smk_known      = "",
  41        .smk_secid      = 6,
  42};
  43
  44struct smack_known smack_known_web = {
  45        .smk_known      = "@",
  46        .smk_secid      = 7,
  47};
  48
  49LIST_HEAD(smack_known_list);
  50
  51/*
  52 * The initial value needs to be bigger than any of the
  53 * known values above.
  54 */
  55static u32 smack_next_secid = 10;
  56
  57/*
  58 * what events do we log
  59 * can be overwritten at run-time by /smack/logging
  60 */
  61int log_policy = SMACK_AUDIT_DENIED;
  62
  63/**
  64 * smk_access_entry - look up matching access rule
  65 * @subject_label: a pointer to the subject's Smack label
  66 * @object_label: a pointer to the object's Smack label
  67 * @rule_list: the list of rules to search
  68 *
  69 * This function looks up the subject/object pair in the
  70 * access rule list and returns the access mode. If no
  71 * entry is found returns -ENOENT.
  72 *
  73 * NOTE:
  74 *
  75 * Earlier versions of this function allowed for labels that
  76 * were not on the label list. This was done to allow for
  77 * labels to come over the network that had never been seen
  78 * before on this host. Unless the receiving socket has the
  79 * star label this will always result in a failure check. The
  80 * star labeled socket case is now handled in the networking
  81 * hooks so there is no case where the label is not on the
  82 * label list. Checking to see if the address of two labels
  83 * is the same is now a reliable test.
  84 *
  85 * Do the object check first because that is more
  86 * likely to differ.
  87 *
  88 * Allowing write access implies allowing locking.
  89 */
  90int smk_access_entry(char *subject_label, char *object_label,
  91                        struct list_head *rule_list)
  92{
  93        int may = -ENOENT;
  94        struct smack_rule *srp;
  95
  96        list_for_each_entry_rcu(srp, rule_list, list) {
  97                if (srp->smk_object == object_label &&
  98                    srp->smk_subject->smk_known == subject_label) {
  99                        may = srp->smk_access;
 100                        break;
 101                }
 102        }
 103
 104        /*
 105         * MAY_WRITE implies MAY_LOCK.
 106         */
 107        if ((may & MAY_WRITE) == MAY_WRITE)
 108                may |= MAY_LOCK;
 109        return may;
 110}
 111
 112/**
 113 * smk_access - determine if a subject has a specific access to an object
 114 * @subject_known: a pointer to the subject's Smack label entry
 115 * @object_label: a pointer to the object's Smack label
 116 * @request: the access requested, in "MAY" format
 117 * @a : a pointer to the audit data
 118 *
 119 * This function looks up the subject/object pair in the
 120 * access rule list and returns 0 if the access is permitted,
 121 * non zero otherwise.
 122 *
 123 * Smack labels are shared on smack_list
 124 */
 125int smk_access(struct smack_known *subject_known, char *object_label,
 126                int request, struct smk_audit_info *a)
 127{
 128        int may = MAY_NOT;
 129        int rc = 0;
 130
 131        /*
 132         * Hardcoded comparisons.
 133         *
 134         * A star subject can't access any object.
 135         */
 136        if (subject_known == &smack_known_star) {
 137                rc = -EACCES;
 138                goto out_audit;
 139        }
 140        /*
 141         * An internet object can be accessed by any subject.
 142         * Tasks cannot be assigned the internet label.
 143         * An internet subject can access any object.
 144         */
 145        if (object_label == smack_known_web.smk_known ||
 146            subject_known == &smack_known_web)
 147                goto out_audit;
 148        /*
 149         * A star object can be accessed by any subject.
 150         */
 151        if (object_label == smack_known_star.smk_known)
 152                goto out_audit;
 153        /*
 154         * An object can be accessed in any way by a subject
 155         * with the same label.
 156         */
 157        if (subject_known->smk_known == object_label)
 158                goto out_audit;
 159        /*
 160         * A hat subject can read any object.
 161         * A floor object can be read by any subject.
 162         */
 163        if ((request & MAY_ANYREAD) == request) {
 164                if (object_label == smack_known_floor.smk_known)
 165                        goto out_audit;
 166                if (subject_known == &smack_known_hat)
 167                        goto out_audit;
 168        }
 169        /*
 170         * Beyond here an explicit relationship is required.
 171         * If the requested access is contained in the available
 172         * access (e.g. read is included in readwrite) it's
 173         * good. A negative response from smk_access_entry()
 174         * indicates there is no entry for this pair.
 175         */
 176        rcu_read_lock();
 177        may = smk_access_entry(subject_known->smk_known, object_label,
 178                                &subject_known->smk_rules);
 179        rcu_read_unlock();
 180
 181        if (may > 0 && (request & may) == request)
 182                goto out_audit;
 183
 184        rc = -EACCES;
 185out_audit:
 186#ifdef CONFIG_AUDIT
 187        if (a)
 188                smack_log(subject_known->smk_known, object_label, request,
 189                                rc, a);
 190#endif
 191        return rc;
 192}
 193
 194/**
 195 * smk_curacc - determine if current has a specific access to an object
 196 * @obj_label: a pointer to the object's Smack label
 197 * @mode: the access requested, in "MAY" format
 198 * @a : common audit data
 199 *
 200 * This function checks the current subject label/object label pair
 201 * in the access rule list and returns 0 if the access is permitted,
 202 * non zero otherwise. It allows that current may have the capability
 203 * to override the rules.
 204 */
 205int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
 206{
 207        struct task_smack *tsp = current_security();
 208        struct smack_known *skp = smk_of_task(tsp);
 209        int may;
 210        int rc;
 211
 212        /*
 213         * Check the global rule list
 214         */
 215        rc = smk_access(skp, obj_label, mode, NULL);
 216        if (rc == 0) {
 217                /*
 218                 * If there is an entry in the task's rule list
 219                 * it can further restrict access.
 220                 */
 221                may = smk_access_entry(skp->smk_known, obj_label,
 222                                        &tsp->smk_rules);
 223                if (may < 0)
 224                        goto out_audit;
 225                if ((mode & may) == mode)
 226                        goto out_audit;
 227                rc = -EACCES;
 228        }
 229
 230        /*
 231         * Allow for priviliged to override policy.
 232         */
 233        if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE))
 234                rc = 0;
 235
 236out_audit:
 237#ifdef CONFIG_AUDIT
 238        if (a)
 239                smack_log(skp->smk_known, obj_label, mode, rc, a);
 240#endif
 241        return rc;
 242}
 243
 244#ifdef CONFIG_AUDIT
 245/**
 246 * smack_str_from_perm : helper to transalate an int to a
 247 * readable string
 248 * @string : the string to fill
 249 * @access : the int
 250 *
 251 */
 252static inline void smack_str_from_perm(char *string, int access)
 253{
 254        int i = 0;
 255
 256        if (access & MAY_READ)
 257                string[i++] = 'r';
 258        if (access & MAY_WRITE)
 259                string[i++] = 'w';
 260        if (access & MAY_EXEC)
 261                string[i++] = 'x';
 262        if (access & MAY_APPEND)
 263                string[i++] = 'a';
 264        if (access & MAY_TRANSMUTE)
 265                string[i++] = 't';
 266        if (access & MAY_LOCK)
 267                string[i++] = 'l';
 268        string[i] = '\0';
 269}
 270/**
 271 * smack_log_callback - SMACK specific information
 272 * will be called by generic audit code
 273 * @ab : the audit_buffer
 274 * @a  : audit_data
 275 *
 276 */
 277static void smack_log_callback(struct audit_buffer *ab, void *a)
 278{
 279        struct common_audit_data *ad = a;
 280        struct smack_audit_data *sad = ad->smack_audit_data;
 281        audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
 282                         ad->smack_audit_data->function,
 283                         sad->result ? "denied" : "granted");
 284        audit_log_format(ab, " subject=");
 285        audit_log_untrustedstring(ab, sad->subject);
 286        audit_log_format(ab, " object=");
 287        audit_log_untrustedstring(ab, sad->object);
 288        audit_log_format(ab, " requested=%s", sad->request);
 289}
 290
 291/**
 292 *  smack_log - Audit the granting or denial of permissions.
 293 *  @subject_label : smack label of the requester
 294 *  @object_label  : smack label of the object being accessed
 295 *  @request: requested permissions
 296 *  @result: result from smk_access
 297 *  @a:  auxiliary audit data
 298 *
 299 * Audit the granting or denial of permissions in accordance
 300 * with the policy.
 301 */
 302void smack_log(char *subject_label, char *object_label, int request,
 303               int result, struct smk_audit_info *ad)
 304{
 305        char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
 306        struct smack_audit_data *sad;
 307        struct common_audit_data *a = &ad->a;
 308
 309        /* check if we have to log the current event */
 310        if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
 311                return;
 312        if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
 313                return;
 314
 315        sad = a->smack_audit_data;
 316
 317        if (sad->function == NULL)
 318                sad->function = "unknown";
 319
 320        /* end preparing the audit data */
 321        smack_str_from_perm(request_buffer, request);
 322        sad->subject = subject_label;
 323        sad->object  = object_label;
 324        sad->request = request_buffer;
 325        sad->result  = result;
 326
 327        common_lsm_audit(a, smack_log_callback, NULL);
 328}
 329#else /* #ifdef CONFIG_AUDIT */
 330void smack_log(char *subject_label, char *object_label, int request,
 331               int result, struct smk_audit_info *ad)
 332{
 333}
 334#endif
 335
 336DEFINE_MUTEX(smack_known_lock);
 337
 338struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
 339
 340/**
 341 * smk_insert_entry - insert a smack label into a hash map,
 342 *
 343 * this function must be called under smack_known_lock
 344 */
 345void smk_insert_entry(struct smack_known *skp)
 346{
 347        unsigned int hash;
 348        struct hlist_head *head;
 349
 350        hash = full_name_hash(skp->smk_known, strlen(skp->smk_known));
 351        head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
 352
 353        hlist_add_head_rcu(&skp->smk_hashed, head);
 354        list_add_rcu(&skp->list, &smack_known_list);
 355}
 356
 357/**
 358 * smk_find_entry - find a label on the list, return the list entry
 359 * @string: a text string that might be a Smack label
 360 *
 361 * Returns a pointer to the entry in the label list that
 362 * matches the passed string.
 363 */
 364struct smack_known *smk_find_entry(const char *string)
 365{
 366        unsigned int hash;
 367        struct hlist_head *head;
 368        struct smack_known *skp;
 369
 370        hash = full_name_hash(string, strlen(string));
 371        head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
 372
 373        hlist_for_each_entry_rcu(skp, head, smk_hashed)
 374                if (strcmp(skp->smk_known, string) == 0)
 375                        return skp;
 376
 377        return NULL;
 378}
 379
 380/**
 381 * smk_parse_smack - parse smack label from a text string
 382 * @string: a text string that might contain a Smack label
 383 * @len: the maximum size, or zero if it is NULL terminated.
 384 *
 385 * Returns a pointer to the clean label, or NULL
 386 */
 387char *smk_parse_smack(const char *string, int len)
 388{
 389        char *smack;
 390        int i;
 391
 392        if (len <= 0)
 393                len = strlen(string) + 1;
 394
 395        /*
 396         * Reserve a leading '-' as an indicator that
 397         * this isn't a label, but an option to interfaces
 398         * including /smack/cipso and /smack/cipso2
 399         */
 400        if (string[0] == '-')
 401                return NULL;
 402
 403        for (i = 0; i < len; i++)
 404                if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
 405                    string[i] == '"' || string[i] == '\\' || string[i] == '\'')
 406                        break;
 407
 408        if (i == 0 || i >= SMK_LONGLABEL)
 409                return NULL;
 410
 411        smack = kzalloc(i + 1, GFP_KERNEL);
 412        if (smack != NULL) {
 413                strncpy(smack, string, i + 1);
 414                smack[i] = '\0';
 415        }
 416        return smack;
 417}
 418
 419/**
 420 * smk_netlbl_mls - convert a catset to netlabel mls categories
 421 * @catset: the Smack categories
 422 * @sap: where to put the netlabel categories
 423 *
 424 * Allocates and fills attr.mls
 425 * Returns 0 on success, error code on failure.
 426 */
 427int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
 428                        int len)
 429{
 430        unsigned char *cp;
 431        unsigned char m;
 432        int cat;
 433        int rc;
 434        int byte;
 435
 436        sap->flags |= NETLBL_SECATTR_MLS_CAT;
 437        sap->attr.mls.lvl = level;
 438        sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
 439        if (!sap->attr.mls.cat)
 440                return -ENOMEM;
 441        sap->attr.mls.cat->startbit = 0;
 442
 443        for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
 444                for (m = 0x80; m != 0; m >>= 1, cat++) {
 445                        if ((m & *cp) == 0)
 446                                continue;
 447                        rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
 448                                                          cat, GFP_ATOMIC);
 449                        if (rc < 0) {
 450                                netlbl_secattr_catmap_free(sap->attr.mls.cat);
 451                                return rc;
 452                        }
 453                }
 454
 455        return 0;
 456}
 457
 458/**
 459 * smk_import_entry - import a label, return the list entry
 460 * @string: a text string that might be a Smack label
 461 * @len: the maximum size, or zero if it is NULL terminated.
 462 *
 463 * Returns a pointer to the entry in the label list that
 464 * matches the passed string, adding it if necessary.
 465 */
 466struct smack_known *smk_import_entry(const char *string, int len)
 467{
 468        struct smack_known *skp;
 469        char *smack;
 470        int slen;
 471        int rc;
 472
 473        smack = smk_parse_smack(string, len);
 474        if (smack == NULL)
 475                return NULL;
 476
 477        mutex_lock(&smack_known_lock);
 478
 479        skp = smk_find_entry(smack);
 480        if (skp != NULL)
 481                goto freeout;
 482
 483        skp = kzalloc(sizeof(*skp), GFP_KERNEL);
 484        if (skp == NULL)
 485                goto freeout;
 486
 487        skp->smk_known = smack;
 488        skp->smk_secid = smack_next_secid++;
 489        skp->smk_netlabel.domain = skp->smk_known;
 490        skp->smk_netlabel.flags =
 491                NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
 492        /*
 493         * If direct labeling works use it.
 494         * Otherwise use mapped labeling.
 495         */
 496        slen = strlen(smack);
 497        if (slen < SMK_CIPSOLEN)
 498                rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
 499                               &skp->smk_netlabel, slen);
 500        else
 501                rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
 502                               &skp->smk_netlabel, sizeof(skp->smk_secid));
 503
 504        if (rc >= 0) {
 505                INIT_LIST_HEAD(&skp->smk_rules);
 506                mutex_init(&skp->smk_rules_lock);
 507                /*
 508                 * Make sure that the entry is actually
 509                 * filled before putting it on the list.
 510                 */
 511                smk_insert_entry(skp);
 512                goto unlockout;
 513        }
 514        /*
 515         * smk_netlbl_mls failed.
 516         */
 517        kfree(skp);
 518        skp = NULL;
 519freeout:
 520        kfree(smack);
 521unlockout:
 522        mutex_unlock(&smack_known_lock);
 523
 524        return skp;
 525}
 526
 527/**
 528 * smk_import - import a smack label
 529 * @string: a text string that might be a Smack label
 530 * @len: the maximum size, or zero if it is NULL terminated.
 531 *
 532 * Returns a pointer to the label in the label list that
 533 * matches the passed string, adding it if necessary.
 534 */
 535char *smk_import(const char *string, int len)
 536{
 537        struct smack_known *skp;
 538
 539        /* labels cannot begin with a '-' */
 540        if (string[0] == '-')
 541                return NULL;
 542        skp = smk_import_entry(string, len);
 543        if (skp == NULL)
 544                return NULL;
 545        return skp->smk_known;
 546}
 547
 548/**
 549 * smack_from_secid - find the Smack label associated with a secid
 550 * @secid: an integer that might be associated with a Smack label
 551 *
 552 * Returns a pointer to the appropriate Smack label entry if there is one,
 553 * otherwise a pointer to the invalid Smack label.
 554 */
 555struct smack_known *smack_from_secid(const u32 secid)
 556{
 557        struct smack_known *skp;
 558
 559        rcu_read_lock();
 560        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 561                if (skp->smk_secid == secid) {
 562                        rcu_read_unlock();
 563                        return skp;
 564                }
 565        }
 566
 567        /*
 568         * If we got this far someone asked for the translation
 569         * of a secid that is not on the list.
 570         */
 571        rcu_read_unlock();
 572        return &smack_known_invalid;
 573}
 574
 575/**
 576 * smack_to_secid - find the secid associated with a Smack label
 577 * @smack: the Smack label
 578 *
 579 * Returns the appropriate secid if there is one,
 580 * otherwise 0
 581 */
 582u32 smack_to_secid(const char *smack)
 583{
 584        struct smack_known *skp = smk_find_entry(smack);
 585
 586        if (skp == NULL)
 587                return 0;
 588        return skp->smk_secid;
 589}
 590