The Windows Access Control Model

http://www.codeproject.com/KB/winsdk/accessctrl1.aspx

http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

http://www.codeproject.com/KB/system/accessctrl3.aspx

http://www.codeproject.com/KB/winsdk/accessctrl4.aspx

摘自于 www.codeproject.com 上的一篇文章,以上为相关链接。

 

The Windows Access Control Model

Windows访问控制单元

 

Part 1 

第一部分

 

Written By oshah | 26 Jun 2005

 

Figure 1: The ACL Editor

 

 Introduction

简介

 

 

Access Control is not available in 16-bit Windows or Windows 95/98/ME.

访问控制在16位Windows或Windows 85/98/ME中是不可用的。

 

In this series of articles, I will discuss the Windows Access Control model and its implementation in Windows NT and 2000. I intend this series to be four parts long. I originally intended this series of articles to be just one part long (I was only going to write about the Windows-2000 style ACL editor), but there is so much you need to know about Windows Access Control, I might as well devote an entire series dedicated to the topic.

 

 

在该系列文章中,我会讨论Windows访问控制单元和在Windows NT和2000中它的实现。我把该系列安排为四部分的长度。起初我想把该系列文章做成一个部分长(我原来只是打算写关于Windows-2000风格的ACL编辑器),但是关于Windows访问控制单元,有那么多你需要知晓的知识,我也许要用一整个系列来用于这一主题。

 

 

This first article serves as an introduction to ACL-based Authorization (from a programming point of view) in Windows NT and how the access control model is implemented. If you have never programmed with Windows security, or are just starting, this first article is for you. Although this first article will display structures in C, you should read this article even if you aren't programming in C. It contains concepts relevant to all programmers.

 

 

第一篇文章是基于ACL的Windows NT中权限的介绍(从程序的观点),以及访问控制单元的方法的实现。如果你从未写过Windows 安全 程序,或者只是才开始写,这篇文章就是为你而写。由于在文章中会用C语言显示数据结构,即使你不用C语言写程序,你也应该读一下。它包含了和所有程序员相关的概念。

 

 

This series of articles isn't meant as a primer on security administration (there are plenty of articles, websites, courses and books on that). Rather, it is intended to discuss the Windows NT Security model from a programmatic perspective. I will already assume you know how to set permissions on files, registry keys, and are quite proficient with the ACL editor. I will also assume you know what users are and what groups are.

 

该系列文章不是关于 安全 管理员 的基础(关于这方面,有大量的文章、网站、课程和书籍)。进一步讲,它是从编程角度来讨论Windows NT 安全 单元。我将假设你知道如何设置文件、注册表的权限,精通于ACL编辑器。我也将假设你知道什么是用户,什么是组。

 

Table Of Contents

内容列表

 

The table of contents is for the entire series.

内容列表针对整个系列。

 

  1. Part 1 - Background and Core Concepts. The Access Control Structures
    1. The Security Identifier (SID)
    2. The Security Descriptor (SD)
    3. The Access Control List (ACL)
    4. Choosing a good discretionary access control list
    5. Windows 2000 Inheritance Model
    6. The Token
    7. A Note on the Security Descriptor Definition Language
    8. Coming Up
  2. Part 2 - Basic Access Control programming
    1. Choosing your language
    2. Fun with SIDs
    3. Which Groups are you a member of?
    4. Enabling Token Privileges
    5. Dissecting the Security Descriptor
    6. Walking an Access Control List
    7. Creating an Access Control List
    8. Do I Have Access?
    9. Creating and Editing a Secure Object
    10. Making Your Own Classes Secure
    11. Toy Programs Download
  3. Part 3 - Access Control programming with .NET v2.0
    1. A history of .NET security.
    2. Reading a SID in .NET.
    3. Whoami .NET.
    4. Privilege handling in .NET.
    5. Running as an unprivileged user in .NET.
    6. Obtaining and editing a security descriptor in .NET, and applying it to an object.
    7. Access checks in .NET.
    8. NetAccessControl program and AccessToken class library.
  4. Part 4 - The Windows 2000-style Access Control editor
    1. Features of the ACL editor (ACLUI).
    2. Getting Started.
    3. Implementing the ISecurityInformation Interface.
    4. ISecurityInformation::SetSecurity
    5. Optional Interfaces.
    6. Presenting the interface.
    7. Filepermsbox - A program to display the security descriptor on an NTFS file or folder.
    8. History

1. 第一部分--背景和核心概念。访问控制结构

  1. 安全 标识符(SID)

  2. 安全描述符(SD)

  3. 访问控制列表(ACL)

  4. 选择一个好的无条件访问控制列表

  5. Windows 2000 继承单元

  6. 令牌

  7. 关于 安全描述符 定义语言的说明

  8. 讨论

2. 第二部分 - 基本访问控制 编程

  1. 选择你使用的语言

  2. 用 SID 写函数

  3. 你是哪一组?

  4. 使能令牌特权

  5. 分析安全描述符

  6. 看看访问控制列表

  7. 创建一个访问控制列表

  8. 我有权限吗?

  9. 创建和编辑一个安全对象

  10. 做你自己的安全类

  11. 示例小程序下载

3. 第三部分 - 用.NET v2.0编写访问控制

  1. .NET 安全的历史

  2. 看看.NET中的SID

  3. .NET 我是谁

  4. .NET中的特权处理

  5. 在.NET中作为无权限用户运行

  6. 获得和编辑.NET下的安全描述符,并把它应用于一个对象

  7. 访问.NET 中的检查

  8. NetAccessControl 程序和AccessTokey类库

4. 第四部分 - Windows 2000风格的访问控制编辑器

  1. ACL编辑器(ACLUI)的特性

  2. 开始

  3. 实现ISecurityInformation 接口

  4. ISecurityInformation::SetSecurity

  5. 可选接口

  6. 呈现的界面

  7. Filepermsbox - 显示一个NTFS文件或文件夹的安全描述符的一个程序

  8. 历史

 

 

Background

背景

 

One of the original design goals of Windows NT was to provide a layer that can implement security for the operating system. The way Windows NT implemented security for its objects was through the Access control model. Even role-based security and the .NET classes could not replace this ACL-based security model. ACLs are far too embedded in the NTFS architecture objects, and the registry was to be rendered obsolete (so much so, that .NET v2.0 had to relent and end up supporting the ACL model too).

 

Windows NT 的一个起初的设计目的是提供一个层,该层可以实现操作系统的安全。Windows NT 用于它的对象的安全的方法是通过访问控制单元。即使基于安全和.NET类的角色也不可能取代这个基于ACL的安全单元。目前,ACL嵌入于NTFS结构对象,注册表也即将废弃(如此,.NET v2.0不得不放缓并结束对ACL单元的支持)。

 

1. The Security Identifier (SID)

1. 安全标识符(SID)

 

Before delving into the concepts of authorization, I should take a discussion about the Security Identifier (SID). We humans like to refer to a user by their user name (like "Administrator"). A computer must refer to the user in binary. It recognises a user by means of a hash (which is called a SID). When a user is created, the computer generates a SID for that user and from then on, it will refer to that user by its SID (not its username). To see a list of your own SIDs, open up regedit and expand the HKEY_USERS tree (you will see a list of the SIDs for the currently logged on users). You can convert the raw SID struct into a textual form by using ConvertSidToStringSid(). A textual SID (usually starts with S-1-5-21-...) consists of at least 3 dash-separated values (which stand for revision, authority, sub authority, ID and primary groups).

 

在进入授权概念之前,我应该讨论一下安全标识符(SID)。我们人类乐于用用户名称来指一个用户(就象“Administrator”)。计算机必须用二进制来指该用户。它通过使用一个hash(被称为SID)来识别一个用户。当创建一个用户时,计算机生成一个SID给那个用户,并从此将用它的SID来指那个用户(而不是它的用户名)。要看到你自己的SID列表,打开注册表编辑器并展开HKEY_USERS树(你会看到当前登录的用户的SID列表)。你可以把原始SID结构用ConvertSidToStringSid()转换成文本形式。一个文本SID(常常以S-1-5-21开头)包含至少3个-分隔符的值(其基于版本、权限、子权限、ID和基本组)。

 

A SID can also represent a user group, a computer, a domain or even a forest. Windows maintains a list of hardcoded SIDs called the Well-Known SIDs (they represent accounts like the LocalSystem, or the NetworkService account). You can view a list of these SIDs in the WELL_KNOWN_SID_TYPE enumeration.

 

一个SID也可以表达一个用户组,一个电脑,一个域,甚至一个森林。Windows维护一个称为著名的SID的硬编码SID列表(他们把用户表达为LocalSystem,或NetworkService用户)。你可以在WELL_KNOWN_SID_TYPE枚举类型中看到这些SID。

 

To convert a SID to a username, you would need to call LookupAccountSid(), and LookupAccountName() to convert a user name to a SID. However, most of the security functions accept either a SID or a username (they actually accept a TRUSTEE structure, which is a kind of union of the username and SID).

 

要把一个SID转换为一个用户名,你会需要调用LookupAccountSid(),和LookupAccountName()来把用户名转换为SID。但是,大多数安全函数既接收SID也接收用户名(他们确实接受一个TRUSTEE结构,它是用户名和SID的联合体)。

 

To start off, I will describe the different types of structures you may meet when programming for access control. This article is intended for all programmers (this part contains no code, just structures and programming concepts).

 

为了开始,我会描述你可能在编写访问控制程序的不同类型结构。这篇文章是给所有程序员的(本部分不包含代码,只是数据结构和编程概念)。

 

2. The Security Descriptor (SD)

2. 安全描述符(SD)

 

Windows NT secures its objects by means of a structure called the security descriptor (SECURITY_DESCRIPTOR). The security descriptor is an integral part of the structure from which Windows objects are built. This means every object (be it file objects, registry keys, network shares, mutexes, semaphores, processes, threads, tokens, hardware, services, drivers...) recognized by Windows NT can be secured. The security descriptor structure exists in both kernel mode and user mode (and is identical in both modes). Although this allows for code reuse for both kernel mode and user mode security, it also means the SECURITY_DESCRIPTOR inherits some nasty quirks from kernel mode.

 

Windows NT通过使用安全描述符(SECURITY_DESCRIPTOR)的数据结构来实现安全。安全描述符是用于创建Windows对象的数据结构的一个集成部分。这意味着每一个Windows NT识别的对象(文件对象,注册表键,网络共享,互斥量,信号量,进程,线程,令牌,硬件,服务,驱动器……)都可以做成安全的。安全描述符结构同时存在于核心模块和用户模块(在两个模块中可识别的)。尽管这使得对核心模块和用户模块的安全的代码可以重用,它也意味着SECURITY_DESCRIPTOR从核心模块的继承了令人不快的缺点。

 

If you open up Winnt.h and scroll down to the SECURITY_DESCRIPTOR struct, you'll see the structure of the security descriptor (Fig. 2). This SECURITY_DESCRIPTOR may actually be one of two structs, one is an absolute security descriptor (SECURITY_DESCRIPTOR), and the other is a self-relative security descriptor (Fig. 3 SECURITY_DESCRIPTOR_RELATIVE).

 

如果你打开Winnt.h并转到SECURITY_DESCRIPTOR数据结构,你会看到安全描述符的结构。SECURITY_DESCRIPTOR也许是两个结构中的一个,一个是绝对安全描述符(SECURITY_DESCRIPTOR),另一个是自身相对的安全描述符(SECURITY_DESCRIPTOR——RELATIVE)。

 

typedef struct _SECURITY_DESCRIPTOR
{
    BYTE Revision;             /* currently SECURITY_DESCRIPTOR_REVISION */
    BYTE Sbz1;                 /* 0 */
    SECURITY_DESCRIPTOR_CONTROL Control;
        /* The type of security descriptor */
    PSID Owner;                /* points to a SID (the owner SID) */
    PSID Group;                /* points to a SID (the primary group) */
    PACL Sacl;                 /* An array of discretionary accesses */
    PACL Dacl;                 /* the auditing accesses */
} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;

Figure 2: The Absolute Security Descriptor.

 

Notice that a security descriptor consists of five members (excluding the two version control members):

  • Discretionary Access Control List (Dacl): This is where the permissions of the object are kept (who's allowed access to the object, who's denied).
  • System Access Control List (Sacl): Specifies the type of auditing to be performed on the object. If an auditing event occurs, it will be stored in the Auditing Event Log.
  • Owner: Specifies the owner of the object (as a SID). The owner of the object can always alter the security descriptor, regardless if someone else has locked out access.
  • Group: Specifies the primary group of the object (as a SID). Windows mostly ignores this parameter (it was for POSIX compatibility, but its presence is rather vestigial now).
  • Control: A bunch of flags that make up a 16-bit integer. It can be zero or more of the following flags:
    • SE_DACL_PRESENT: The Dacl member is valid.
    • SE_SACL_PRESENT: The Sacl member is valid.
    • SE_DACL_AUTO_INHERITED: The DACL auto-inherits and gets entries from its parent included in itself.
    • SE_SACL_AUTO_INHERITED: same as SE_DACL_AUTO_INHERITED, but it applies to the SACL.
    • SE_DACL_PROTECTED: If the parent's ACLs conflict with any ACL defined here, override the parent's ACL. Useful for overriding inheritance.
    • SE_SACL_PROTECTED: Same as SE_DACL_PROTECTED, but for SACLs.
    • SE_DACL_DEFAULTED: The Dacl member equals the default DACL for this object type.
    • SE_SACL_DEFAULTED: The Sacl member equals the default SACL for this object type.
    • SE_GROUP_DEFAULTED: The Group member equals the default Group for this object type.
    • SE_OWNER_DEFAULTED: The Owner is defaulted.
    • SE_SELF_RELATIVE: Indicates this security descriptor is self-relative.

 

The last 4 entries of this structure are all pointers, and they point to the buffers where the ACLs etc. can be found. These pointers can point to any valid location in memory (not necessarily in a contiguous block). Because the security descriptors aren't contiguous, it is going to be rather cumbersome to write the security descriptors to disk, or across processes. Microsoft solved this problem by introducing a new structure, called the self-relative security descriptor (Fig. 3 SECURITY_DESCRIPTOR_RELATIVE).

 

/* This is not valid C or C++ code. */
typedef struct _SECURITY_DESCRIPTOR_RELATIVE
{
    BYTE  Revision;    /* currently SECURITY_DESCRIPTOR_REVISION */
    BYTE  Sbz1;        /* 0 */
    SECURITY_DESCRIPTOR_CONTROL Control;
    /* The type of security descriptor */
    DWORD OwnerOffset;
    /** The index of this->Owner. ie.
    *   (this+OwnerOffset) should == this->Owner
    **/

    DWORD GroupOffset; /* The index of this->Group */
    DWORD SaclOffset;  /* The offset to Sacl */
    DWORD DaclOffset;  /* The offset to Dacl*/

    struct SecurityDescriptorData
    {
    /* The data for the security descriptor is after the 4 DWORDs */
        ...            /* padding bytes */
        SID Owner;     /* The SID is stored Inside the structure */
        ...
        SID Group;     /* so are the other parts */
        ...
        ACL Sacl[];
        /* the entries need not appear in this order */
        ...
        ACL Dacl[];
    } ;
} SECURITY_DESCRIPTOR_RELATIVE, *PISECURITY_DESCRIPTOR_RELATIVE;

 

Figure 3: The structure of the relative security descriptor

 

The self-relative security descriptor is much more complicated than the absolute security descriptor (so complex, that you cannot represent it in C++). Although the first three fields are the same as the absolute security descriptor, the security descriptors become very different after that. Following the Control member are four DWORDs (which represent offsets to the data). The data for the security descriptor all follow these four DWORDs. I've inserted padding bytes into the structure to illustrate that the data can appear anywhere in the buffer (simply change the Offset members to move the buffer). The four members do not need to appear in any particular order either. By choosing special offsets, you can make the Owner member appear after the group. At the expense of being horrendously complicated, the self-relative security descriptor occupies one contiguous block, making it suitable for transporting between application boundaries and storage media.

How can you tell the difference between an absolute and self-relative security descriptor? The difference is that with an absolute security descriptor, after the Control member are four pointers to the Group / Owner / SACL / DACL (in that order), and these four pointers point to separate locations in memory where the data can be found. With a self-relative security descriptor, after the Control member are four DWORDs which represent offsets to the Group / Owner / SACL / DACL. For example, if the group member has the value 0xf, then it's telling Windows, "You can find the group SID 0xf bytes after me". 64-bit Windows makes this even more confusing when these two structures have differing sizes!

To make matters worse, Microsoft doesn't distinguish between the absolute or self-relative security descriptor in their docs. Microsoft has reserved the right to make internal changes to the security descriptor, meaning you cannot rely on this structure being the same in future. Instead, if you want to manipulate a security descriptor, you should do so using the authorization APIs. Using the APIs encapsulate the complexity of these structures from you. So to distinguish between an absolute security descriptor and a self-relative one, call GetSecurityDescriptorControl(), and test for the SE_SELF_RELATIVE flag.

It's important that you know which API expects an absolute security descriptor, and which ones expect a self-relative one. In Part 2 [^] of this series, you will get around to actually building a security descriptor.

 

 

3. The Access Control List (ACL)

Whenever you perform an action (e.g. a read) on an object in Windows NT, the action is encoded into a 32 bit integer (called the ACCESS_MASK). The ACCESS_MASK is specific to the object you are trying to create (if you read a file, the ACCESS_MASK would be FILE_GENERIC_READ). When you open the object with the requested ACCESS_MASK, Windows will get your username (inside your thread token) and then start reading the discretionary access control list (obtained from the security descriptor).

The DACL can be thought of as a table of user SIDs, ACCESS_MASKs, and access types. Don't try to code it as an array of structs though. Use the low-level ACL functions GetAce(), GetAclInformation() & friends if you want to parse an ACL. In NT4, Microsoft provides a view of the ACL that looks exactly like a table of SIDs, ACCESS_MASKs and types (the EXPLICIT_ACCESS structure).

 

/* Not valid C or C++ code */
struct ACE
{
    BYTE AceType;     /* can be allow/deny/audit/custom */
    BYTE AceFlags;    /* Inherited? */
    ...
    ACCESS_MASK Mask; /* Specifies the action */
    ...
    SID Sid;          /* associate with this user/group/... */
} ;

struct ACL
{
    BYTE AclRevision;
    ...
    WORD AceCount;                  /* number of ACEs */
    ...
    ACE AceList[this->AceCount];    /* An array of ACEs */
} ;

/* You can think of an ACL as an array of EXPLICIT_ACCESSes */
typedef struct _EXPLICIT_ACCESS
{
    DWORD grfAccessPermissions;     /* ACCESS_MASK */
    ACCESS_MODE grfAccessMode;      /* Allow/deny/audit/custom/... */
    DWORD grfInheritance;           /* Inherited? */
    TRUSTEE Trustee;                /* The SID */
} EXPLICIT_ACCESS, *PEXPLICIT_ACCESS;

Figure 4: An outline of the ACL and the ACE.

If the DACL is a table, then each row in the table is called an Access Control Entry. When an action is performed, Windows will enumerate this list of ACEs to find an entry that refers to you (you being the thread token). Windows enumerates the ACEs in the order they appear in the ACL. At first sight this goes against your intuition, the Help documentation, and what the MCP books say ("I thought Windows walks the Deny ACEs first, then walks the Allow ACEs. Now you're telling me this is wrong?"). Actually, we are both right. When Windows sets the DACL, it sorts the access control entries [^] so that deny ACEs do precede allow ACEs, and the access control model follows what you were taught.

If you make a call to the low level functions, you can circumvent the sorting, and make the ACEs appear in any order you like. The Cygwin tools utilise this technique to create DACLs unordered. However, these DACLs will not obey the access control rules, and you can make files with broken ACLs.

If you (your token) isn't found in the ACL, then the open object function fails (Access denied). If you are found, Windows will look in the ACE to see what you are allowed to do. If the ACE allows you to open the object, you are granted access. If anything fails here, you are denied access (this is an example of a security best practice: "if anything goes wrong, make sure you fail securely").

Now that you have been granted or denied access, Windows will now make a check on the other ACL, the System Access Control List. The difference between an SACL and a DACL is that DACL contains allow / deny entries, but an SACL contains audit entries. The SACL tells Windows which actions to log in the Security Event Log (audit successes / failures). Apart from that, you can treat the SACL / DACL as the same. If Windows finds an SACL which tells it to audit the access, then the access attempt is written to the Event log.

If you want to see if an ACL allows you access to the object, use the AccessCheck() API on the object.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值