69天探索操作系统-第47天:构建安全的内核 - 实现访问控制和认证机制

281 阅读10分钟

pro13.avif

1. 介绍

内核安全机制通过实施访问控制、认证和授权,构成了系统安全的核心。这些机制确保只有经过授权的用户和进程才能访问特定资源,从而保护系统免受未经授权的访问和潜在威胁。内核充当守门人,协调所有访问请求,并确保安全策略在操作系统的最低级别得到执行。

内核安全中的关键概念包括强制访问控制(MAC),它执行由系统管理员定义的严格访问策略,以及自主访问控制(DAC),它允许资源所有者设置访问权限。此外,安全上下文和权限级别用于定义用户、进程和资源的安全属性。这些概念共同作用,构建了一个强大的安全框架,保护系统免受内部和外部威胁。

2. 访问控制实现

访问控制是一种基本的安全机制,它决定了谁可以访问系统中的哪些资源。提供的代码实现了一个基本的访问控制系统,使用安全上下文和访问向量。安全上下文包含有关用户、角色、类型和安全级别的信息,而访问向量定义了访问资源所需的权限。

#include <linux/security.h>
#include <linux/lsm_hooks.h>
#include <linux/cred.h>

struct security_context {
    uint32_t user_id;
    uint32_t role_id;
    uint32_t type;
    uint32_t level;
};

struct access_vector {
    uint32_t permissions;
    struct security_context subject;
    struct security_context object;
};

static int check_permission(struct access_vector *av) {
    if (!av)
        return -EINVAL;

    // Check security level
    if (av->subject.level < av->object.level)
        return -EACCES;

    // Check role-based access
    if (!has_role_permission(av->subject.role_id,
                           av->object.type,
                           av->permissions))
        return -EACCES;

    return 0;
}

static int security_file_permission(struct file *file, int mask) {
    struct security_context *sc;
    struct access_vector av;

    sc = get_security_context(current);
    if (!sc)
        return -EACCES;

    av.subject = *sc;
    av.object = get_file_security_context(file);
    av.permissions = mask;

    return check_permission(&av);
}

check_permission函数是此实现的核心。它首先检查主体的安全级别是否足以访问对象。如果没有,则返回错误。接下来,它验证主体的角色是否具有执行所请求操作所需的权限。这是通过使用 has_role_permission 函数来完成的,该函数检查基于角色的访问控制(RBAC)规则。security_file_permission 函数将此逻辑与文件操作绑定,确保文件访问请求在请求进程的安全上下文进行验证。

3.安全模块架构

Linux安全模块(LSM)框架允许开发人员实现与内核安全基础设施集成的自定义安全模块。提供的代码演示了如何通过定义一个security_operations结构来创建一个自定义的LSM。这个结构包含各种安全钩子的函数指针,例如file_permissionfile_opentask_create,这些函数由内核调用以执行安全策略。

#include <linux/lsm_hooks.h>
#include <linux/security.h>

static struct security_operations custom_security_ops = {
    .name = "custom_lsm",
    .file_permission = security_file_permission,
    .file_open = security_file_open,
    .socket_create = security_socket_create,
    .socket_connect = security_socket_connect,
    .task_create = security_task_create,
    .inode_create = security_inode_create,
    .inode_permission = security_inode_permission,
};

static int __init custom_security_init(void) {
    // Initialize security structures
    if (initialize_security_context() < 0)
        return -EINVAL;

    // Register security operations
    if (register_security(&custom_security_ops))
        return -EINVAL;

    printk(KERN_INFO "Custom LSM initialized\n");
    return 0;
}

security_initcall(custom_security_init);

custom_security_init 函数通过设置必要的安全结构和将安全操作注册到内核中来初始化自定义的 LSM。注册后,LSM 可以拦截和验证访问请求,确保所有操作都符合定义的安全策略。这种模块化方法允许灵活和可扩展的安全实现,使开发人员能够根据具体需求定制安全模型。

4.认证机制

认证是验证用户或进程身份的过程。提供的代码使用身份验证令牌实现了一个身份验证服务。每个令牌包含一个唯一ID、用户ID、过期时间和一个用于确保其完整性的加密哈希。authenticate_user 函数通过验证用户的凭据来处理认证过程,如果凭据有效,则生成一个新的令牌。

struct auth_token {
    uint64_t token_id;
    uint32_t user_id;
    time_t expiration;
    uint32_t flags;
    uint8_t hash[SHA256_DIGEST_SIZE];
};

struct auth_manager {
    struct mutex lock;
    struct list_head tokens;
    struct crypto_shash *hash_tfm;
};

static int authenticate_user(const char *username,
                           const char *credential,
                           struct auth_token **token) {
    struct auth_token *new_token;
    int ret;

    // Verify credentials
    ret = verify_credentials(username, credential);
    if (ret)
        return ret;

    // Create new token
    new_token = kmalloc(sizeof(*new_token), GFP_KERNEL);
    if (!new_token)
        return -ENOMEM;

    // Initialize token
    new_token->token_id = generate_token_id();
    new_token->user_id = get_user_id(username);
    new_token->expiration = get_current_time() + TOKEN_LIFETIME;

    // Calculate token hash
    calculate_token_hash(new_token);

    // Add to active tokens
    mutex_lock(&auth_manager.lock);
    list_add(&new_token->list, &auth_manager.tokens);
    mutex_unlock(&auth_manager.lock);

    *token = new_token;
    return 0;
}

令牌生成过程包括创建一个新的auth_token结构,初始化其字段,并计算一个哈希值以防止篡改。然后将令牌添加到活动令牌列表中,该列表由互斥锁保护以确保线程安全。此实现确保只有经过身份验证的用户才能访问受保护的资源,并提供了一种跟踪活动会话的机制。

5. 授权框架

授权决定了已认证的用户或进程可以执行哪些操作。提供的代码实现了一个基于角色的访问控制(RBAC)框架,其中每个角色都有一组权限和一个访问控制列表(ACL),用于定义在特定对象类型上允许的操作。check_role_permission 函数负责验证访问请求是否符合角色的权限和 ACL。

struct role_entry {
    uint32_t role_id;
    uint32_t permissions;
    struct list_head acl;
};

struct acl_entry {
    uint32_t object_type;
    uint32_t allowed_ops;
    struct list_head list;
};

static int check_role_permission(uint32_t role_id,
                               uint32_t object_type,
                               uint32_t requested_ops) {
    struct role_entry *role;
    struct acl_entry *acl;

    role = find_role(role_id);
    if (!role)
        return -EACCES;

    // Check role permissions
    if (!(role->permissions & ROLE_ACTIVE))
        return -EACCES;

    // Check ACL
    list_for_each_entry(acl, &role->acl, list) {
        if (acl->object_type == object_type) {
            if ((acl->allowed_ops & requested_ops) == requested_ops)
                return 0;
            break;
        }
    }

    return -EACCES;
}

该函数首先从角色表中检索角色的条目,并检查该角色是否处于活动状态。然后,它遍历ACL以找到匹配的对象类型,并验证所请求的操作是否允许。如果检查通过,函数返回成功;否则,返回错误。该框架提供了一种灵活且可扩展的方式来管理权限,使其适用于具有多样化访问需求的复杂系统。

6.审计系统

审计系统对于跟踪安全相关事件和检测潜在违规行为至关重要。提供的代码使用 audit_event 结构实现了一个基本的审计日志机制,该结构包含有关事件的详细信息,例如事件ID、时间戳、类型、结果以及涉及的主体的安全上下文和对象的安全上下文。audit_security_event 函数使用内核的审计子系统记录这些事件。

struct audit_event {
    uint64_t event_id;
    time_t timestamp;
    uint32_t type;
    uint32_t result;
    struct security_context subject;
    struct security_context object;
    char description[256];
};

static int audit_security_event(struct audit_event *event) {
    struct audit_buffer *ab;

    ab = audit_log_start(audit_context(), GFP_KERNEL, event->type);
    if (!ab)
        return -ENOMEM;

    audit_log_format(ab, "event=%llu type=%u result=%u ",
                    event->event_id, event->type, event->result);

    audit_log_format(ab, "subject=(uid=%u,role=%u,level=%u) ",
                    event->subject.user_id,
                    event->subject.role_id,
                    event->subject.level);

    audit_log_format(ab, "object=(type=%u,level=%u) ",
                    event->object.type,
                    event->object.level);

    audit_log_format(ab, "desc=\"%s\"", event->description);

    audit_log_end(ab);
    return 0;
}

该函数创建一个审计缓冲区,并将事件详细信息格式化为可读的字符串。然后,它将格式化的字符串写入审计日志,管理员可以查看该日志以监控系统活动和调查安全事件。此实现确保所有关键安全事件都被记录,为维护系统完整性和合规性提供了宝贵的工具。

7.安全内存管理

安全内存管理对于保护敏感数据免受未经授权的访问至关重要。提供的代码实现了一种安全的缓冲机制,该机制具有额外的安全功能,如加密和页面锁定,用于分配和管理内存区域。allocate_secure_buffer 函数负责安全缓冲区的分配和初始化。

struct secure_buffer {
    void *addr;
    size_t size;
    uint32_t flags;
    struct crypto_cipher *cipher;
};

static int allocate_secure_buffer(struct secure_buffer *buf,
                                size_t size,
                                uint32_t flags) {
    int ret;

    // Allocate memory
    buf->addr = vmalloc(size);
    if (!buf->addr)
        return -ENOMEM;

    buf->size = size;
    buf->flags = flags;

    // Initialize encryption if needed
    if (flags & SECURE_BUFFER_ENCRYPTED) {
        ret = init_buffer_encryption(buf);
        if (ret)
            goto err_free;
    }

    // Lock pages in memory if needed
    if (flags & SECURE_BUFFER_LOCKED) {
        ret = lock_buffer_pages(buf);
        if (ret)
            goto err_crypto;
    }

    return 0;

err_crypto:
    if (buf->cipher)
        crypto_free_cipher(buf->cipher);
err_free:
    vfree(buf->addr);
    return ret;
}

该函数首先使用vmalloc分配内存并初始化缓冲区的字段。如果缓冲区被标记为加密,则设置加密密码来加密数据。如果缓冲区被标记为锁定,则锁定内存中的页面以防止它们被交换出去。这些功能确保即使在系统被攻破的情况下,敏感数据也能得到保护。

8. 加密服务

加密服务对于保护数据在传输和静止状态下的安全至关重要。提供的代码使用crypto_service结构实现了一个基本的加密服务,该结构包含一个对称密钥密码和一个哈希函数。encrypt_data函数演示了如何使用内核的加密API来加密数据。

struct crypto_service {
    struct crypto_skcipher *tfm;
    struct crypto_shash *hash;
    u8 key[32];
    u8 iv[16];
};

static int encrypt_data(struct crypto_service *cs,
                       const void *src,
                       void *dst,
                       size_t size) {
    struct skcipher_request *req;
    struct scatterlist sg_in, sg_out;
    DECLARE_CRYPTO_WAIT(wait);
    int ret;

    // Prepare scatter-gather lists
    sg_init_one(&sg_in, src, size);
    sg_init_one(&sg_out, dst, size);

    // Create encryption request
    req = skcipher_request_alloc(cs->tfm, GFP_KERNEL);
    if (!req)
        return -ENOMEM;

    skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                crypto_req_done, &wait);
    skcipher_request_set_crypt(req, &sg_in, &sg_out, size, cs->iv);

    // Perform encryption
    ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);

    skcipher_request_free(req);
    return ret;
}

该函数为输入和输出数据准备散列-收集列表,并创建对称密钥加密请求。然后设置加密参数,包括初始化向量(IV),并执行加密操作。加密数据被写入输出缓冲区,确保其保持机密性和不可篡改性。

9. 安全监控

安全监控是持续观察系统活动以检测和响应潜在威胁的过程。提供的代码使用 security_monitor 结构实现了一个安全监控机制,该结构维护了一个安全事件队列和一个安全警报计数器。monitor_security_event 函数将事件添加到队列中,并检查是否存在安全违规。

struct security_monitor {
    struct mutex lock;
    struct list_head events;
    struct work_struct work;
    atomic_t alert_count;
};

static void monitor_security_event(struct security_monitor *monitor,
                                 struct security_event *event) {
    unsigned long flags;

    // Add event to queue
    spin_lock_irqsave(&monitor->lock, flags);
    list_add_tail(&event->list, &monitor->events);
    spin_unlock_irqrestore(&monitor->lock, flags);

    // Check for security violations
    if (is_security_violation(event)) {
        atomic_inc(&monitor->alert_count);
        schedule_work(&monitor->work);
    }
}

static void security_alert_handler(struct work_struct *work) {
    struct security_monitor *monitor =
        container_of(work, struct security_monitor, work);

    // Process security alerts
    process_security_alerts(monitor);

    // Notify security admin
    send_security_notification(monitor);
}

如果检测到违规行为,该函数会递增警报计数器并安排一个工作项来处理警报。security_alert_handler 函数处理警报并通知安全管理员,确保潜在威胁得到及时处理。这种实现方式提供了主动的安全方法,使管理员能够在事件升级之前做出响应。

10. 总结

在内核中实现安全机制需要对访问控制、身份验证和监控有全面的理解。提供的代码演示了如何构建一个强大的安全框架,以保护系统资源和确保数据完整性。通过精心设计和实现这些机制,开发人员可以创建能够抵御内部和外部威胁的安全系统。