Guaranteed the technology of private object security in the app

xiaoxiao2021-04-04  265

Token and its concept

The content of this article is all relevant to management access control, and management access control is also known as authorization. Before discussing this issue, we need a way to identify users who have visited the resource taken by security measures. This is the role of the token. The token represents the login session in the computer. The login session is created as long as the user accesses the computer in an interactive or remote manner. The token is a handler that can be used to query and manipulate the login session. If you have a token, you can get the users represented by the login session, and determine whether to grant the user to access the resources that have taken the resource taken.

Since all applications run in the context of the login session, some types of tokens can always be used to indicate the user. At any given moment, there may be one or more different tokens or security contexts, which will make people confuse. Each login session represents different users. At least one token is attached to the process. You can get this token using the OpenProcessToken function.

Chandle token;

Helpers :: Checkerror (:: getcurrentprocess (:: getcurrentprocess (),

Token_Query,

& token.m_h));

Chandle is a packaging class provided by the Active Outlet (ATL) that is used to ensure that the handler can be turned off by calling the CloseHandle function when the token is out of the range. Checkerrou is a helper function in my helpers namespace. CheckerRrou throws a HRESULT for explaining the mistakes that occur. Using different methods can indicate an error in Windows C language programming, I tend to standardize HRESULT to ensure consistency. If you download this article, you can use the Helpers named space. The return value of the GetCurrentProcess function is a pseudo-handler that represents the current process. Because it is not a real handler, there is no need to call the CloseHandle function to release the returnful handle.

Another token that can be used is a threaded loan. You can retrieve the threaded token by calling the OpenThreadToken function.

Chandle token;

Helpers :: Checkerror (:: OpenThreadToken (:: getcurrentthread (),

Token_Query,

True, // Open itself

& token.m_h));

As with getCurrentProcess, getCurrentThread also returns a pseudo-handler, so there is no need to call CloseHandle for the program. Unlike OpenProcessToken, if no token is associated with the current thread, you may not successfully call OpenThreadToken, then the function returns ERROR_NO_TOKEN.

In some cases, there is even a third token that represents other security contexts. For example, ASP.NET allows closing customer simulation, in which case, the customer ID can be obtained via the HTTPContext class.

After getting the token, if you can use it to perform some interesting operations, we will help us understand it. The simplest operation that can be performed using the token is to query it to obtain information about the login session. You can do this using the GetTokenInformation function. Because the GetTokenformation function can be used to query different types of information, the calling method is quite complicated, so I wrote a packaging function template to reduce the error that may occur when calling. The following example illustrates a query token to obtain a method of obtaining token user information.

ClocalMemoryt tokenuser (helpers :: gettokenformation );

CCOMBSTR STRING = Helpers :: ConvertsidTostringsid (Tokenuser-> User.SID);

The ConvertSidToStringsid Helper function is used to convert the binary security identifier (SID) to the user-friendly string. Using the SID means that the user account is a computer that is easy to identify. If your interest is only in the functionality of the package, you can download and view the source code attached herein. The content regarding the CLOCALMEMORYT template will be discussed after the memory management is introduced.

Safety descriptor foundation

Understand how to identify users, we need a way to manage different permissions from different users. This is the need to use the safety descriptor. The security descriptor contains many different types of information. The most interesting is the Owner Safety Identifier (SID) and two access control lists (ACLs). The owner SID can identify users with objects. Two ACLs are random ACLs and system ACLs, respectively. Because the system ACL is actually independent of access control, this article focuses on the random ACL (DACL).

The security descriptor is represented by the Security_Descriptor structure because there is no description regarding this structure, so it should be avoided directly to manipulate it. Microsoft provides a number of simple functions for querying and manipulating a security descriptor for built-in objects (such as files, and registry entries). The following example illustrates the method of obtaining the owner SID and DACL of the Program Files directory in the local computer.

ClocalMemory SecurityDescriptor;

PSID SID = 0;

PACL DACL = 0;

Helpers :: Checkerror (:: getnamedsecurityInfo),

SE_FILE_OBJECT,

Owner_security_information | DACL_SECURITY_INFORMATION,

& SID,

0, // group

& DACL,

0, // SACL

& securityDescriptor.m_ptr));

GetNamedSecurityInfo is a function with a variety of uses. It allows for the vast majority (if all queries do not require all queries). The first parameter is the name (or path) of the object to be queried. The second parameter indicates the type of object. This example queries the file system object. For example, if you want to query the registry object, you can change it to SE_REGISTRY_KEY. The next step is to specify the information of interest using the parameter security_information of the enumerated type. The other four parameters are respective to the four main parts of the security descriptor, respectively. The convenience here is that the corresponding parameters can be transmitted to the corresponding parameters for those that are not interested. The last parameter is a pointer to the security descriptor itself, which is actually a copy of the security descriptor, and must be released using the localfree function.

You can also use a function called setNameDSecurityInfo to update the security descriptor of the built-in object. Because this function is the same as described above, it is not an in-depth analysis here.

Private security descriptor

GetNameDsecurityInfo and SetNameDSecurityInfo have a strong feature for processing built-in objects. But what is the function of private objects (for example, the objects used in your application business logic)? Can you expand these functions to support private objects? Unfortunately, the answer is negative. Because each resource (such as file system or registry) defines how to reserve security descriptors, these functions cannot understand the security information of the query or set the private object you created. Fortunately, we have a solution. First we need a way to create a private security descriptor. Using the CreatePrivateObjectSecurityEx function can create your own security descriptor from the beginning. This function is quite flexible, and its main use has two: create a new security descriptor and the inheritance of updating an existing security descriptor. Its prototype is as follows.

Bool CreatePrivateObjectSecurityEx (Psecurity_Descriptor ParentDescriptor,

Psecurity_Descriptor defaultdescriptor,

Psecurity_Descriptor * NewDescriptor,

GUID * TYPE,

Bool iscontainer,

Ulong Autoinheritflags,

Handle token,

PGENERIC_MAPPING GenericMapping;

ParentDescriptor is used to indicate the parent object that will inherit its ACL. If the object does not have a parent object, you can only pass the parameters. The main purpose of DefaultDescriptor is that when the ParentDescriptor changes, use the inheritable access control item (ACE) to update the existing security descriptor. The method of achieving this is to create a new security descriptor that will be returned by the NewDescriptor parameter and then release the original security descriptor. To pass an explicit item of an object ACL, transfer the existing security descriptor as a defaultDescriptor parameter.

TYPE will be used when processing the security of the Active Directory object. To indicate whether the object represented by the security descriptor is a container of other security objects, you can use IsContainer. To affect the way each tolerant ACE points to the newly built security descriptor, you can use Autoinheritflags. SEF_DACL_AUTO_INHERIT can be transmitted to inherit any inherited ACE. However, when you get the inherited ACE according to the parent security descriptor, you should also include the SEF_AVOID_PRIVILEGE_CHECK and SEF_AVOID_OWNER_CHECK flags to avoid access checking for users, because when updating only inherits, there is no need to do this. For more information on managing ACL inheritance, see the book of Keith Brown Programming Windows Security (English).

To identify users who create objects, you can use the token to get the default value of the newly created security descriptor (for example, the owner's SID). It is convenient to operate to the server application in an explicit delivery user token, because it is not required to simulate.

Finally, genericmapping is used to provide information on explicitly transmitted information, which can be mapped to four general rights, ie read, write, execute, and all for specific objects. The content of the permissions will be discussed in the next part.

After the work of the security descriptor is completed, it must be released by calling DestroyPrivateObjectSecurity.

Now we can create your own security descriptor, we need to find a way to query and modify it. Although GetNamedSecurityInfo and SetNameDSecurityInfo cannot be used to operate private objects, there is still functions that can achieve this. To modify a private security descriptor, you need to use the SetPrivateObjectSecurityEx function. The prototype of this function is as follows. Bool SetPrivateObjectSecurityEx (Security_Information SecurityInformation,

Psecurity_Descriptor ModificationDescriptor,

Psecurity_Descriptor * SecurityDescriptor,

Ulong Autoinheritflags,

PGeneric_mapping genericmapping,

Handle token;

The documentation about the function has an error, which is to set the securityDescriptor parameter to [OUT]. This parameter should be set to [IN, OUT] because this parameter must point to a valid security descriptor. If necessary, SetPrivateObjectSecurityEX will call LocalReallo to assign enough memory cells and write new information. This is the reason for the pointer to the security descriptor pointer, and after calling setPrivateObjectSecurityEx, the memory location points to the SECURITYDESCRIPTOR may change.

As you can see, SetPrivateObjectSecurityEX does not provide a single parameter for setting the security descriptor, which is the same as setnamedsecurityInfo. SetPrivateObjectSecurityEx requires an existing security descriptor for replication values. Fortunately, create a security description object in the stack and use information that may copy it into a private security descriptor. This security descriptor is relatively easy. Here is an example:

CWELLKNOWNSID Adminsid = CWELLKNOWNSID :: administrators ();

Security_descriptor templatedescriptor = {0};

Helpers :: Checkerror (:: InitializeSecurityDescriptor (& TemplatedScriptor,

Security_Descriptor_revision));

Helpers :: Checkerror (:: SetSecurityDescriptorowner (& TemplatedScriptor,

& adminsid,

False);

Helpers :: Checkerror (:: setPrivateObjectSecurityEx (Owner_Security_Information,

& TemplatedScriptor,

& securityDescriptor,

SEF_AVOID_PRIVILE_CHECK,

& genermapping,

0)));

Where TemplatedScriptor is a stack-based security descriptor. Please note that the memory unit is cleared before proceeding. INITIALIZESECURITYDESCRIPTOR is used to set the revision level, otherwise the security descriptor will be empty. SetSecurityDescriptorowner sets the owner SID as well known local administrators group. This is achieved with the help of the CWELLKNOWNSID class, which can be obtained when downloading this article. Finally, SETPRIVATEOBJECTSECURityEx is called to copy the owner information from our template descriptor to the private security descriptor included in SecurityDescriptor. You may be strange, why not directly use these functions to set the part of the private security descriptor. For security descriptors, there are two different memory formats. The absolute security descriptor contains pointers that point to the security information it contain. The memory is separately allocated, separated from the memory allocation occupied by the security descriptor structure. The relative safety descriptor stores all of its information in a continuous memory block. It does not store the pointer, but stores the offset in the memory block.

After understanding the different ways of security descriptors exists in memory, things understand. Private security descriptors are typically relative security descriptors. This is why the function to deal with such security descriptors is more complicated. Creating a security descriptor in the stack is relatively easy, because such security descriptors are absolute security descriptors, such as SetSecurityDescriptorowner, such as SETSecurityDescriptorowner, only need to set pool values ​​in the stack-based security descriptor.

Fortunately, query private security descript is very simple. You can get different parts of the content using standard functions (such as GetSecurityDescriptorowner and GetSecurityDescriptOrdaDACL). There is also a function called getPrivateObjectSecurity, but it is not often used to query security descriptors. When using the Access Control Editor, it is convenient to use this function (this problem will be discussed later).

Authority

Permissions are also known as access rights, which are mentioned earlier. The topic about it is also wonderful with memory management. However, it is important to understand how to design permissions for your private object. Permissions are used each time you call functions that may access secure resources. For example, the CreateFile function we are familiar with has a parameter using access bitmask, which represents a specific permission.

You must also define specific permissions for your objects to define specific permissions (for example, file_append_data, and file_traverse). For 32-bit access masks, 16 bits are only for specific permissions. After the subject defines a specific permissions, it needs to be classified into four general rights categories, respectively. General Permissions are generic_read, generic_write, generic_execute, and generic_all. In this way, the programmer can simply declare the need to read access and the application logic is mapped to the permission to read access categories. However, because any security function cannot know the general rights category mapped by specific permissions, you need to fill in a generic_mapping structure that will be passed to many security functions.

After a basic understanding of the permissions, we can define the following permissions for a specific Widget resource.

Namespace permissions

{

Const dword addwidget = 0x0001;

Const dword Listwidgets = 0x0002;

Const dword renamewidget = 0x0004;

Const dword readwidgetdata = 0x0008;

Const DWORD WRITEWIDGETDATA = 0x0010; Const Dword GenericRead = Standard_Rights_Read |

Listwidgets |

Readwidgetdata;

Const dword genericwrite = standard_rights_write |

AddWidget |

RenameWidget |

Writewidgetdata;

Const dword genericExecute = standard_rights_execute;

Const dword genericall = standard_rights_required |

GenericRead |

GenericWrite |

Genericexecute;

}

In this way, you should be able to fill the global generic_mapping structure and deliver pointers to it, and transfer pointers to all functions that need this structure. Now the programmer can use only common permissions (for example, generic_write), and the general relief maps to our Widget's Permissions :: genericRead. Careful programmers may not assume that users have all permissions. In this case, he may use one of specific permissions (for example, Permissions :: RenameWidget). Of course, it is still possible to use a standard permissions (for example, DELETE, if we specify a specific meaning for it in Widget).

Access control list

DACL is the core of the safety descriptor. Each Access Control (ACE) in the list defines a specific user or group permission to a resource. ACE can be either positive or negative. The positive ACE indicates a user or group awarded a specific permissions, and the negative ACE indicates that the permissions will be rejected. If a user has not appeared in the ACL (whether the member appears or as a member of the group), then any access operation of the user will be rejected.

ACL is stored in a continuous memory block, which consists of an ACL structure and a list of sorted ACEs. The following example illustrates a method of creating a simple ACL.

ClocalMemoryt ACL (100);

Helpers :: Checkerror (:: InitializeaCl (acl.m_ptr,

100,

ACL_REVISION));

DWORD inheritflags = container_inherit_ace | Object_inherit_ace;

CWELLKNOWNSID EVERYONESID = CWELLKNOWNSID :: Everyone ();

Helpers :: CheckerroRROR (:: addaccessallowedaceex (acl.m_ptr,

ACL_REVISION,

Inheritflags,

Generic_read,

& everyOsid));

ClocalMemoryt usersid (helpers :: getusersid (token);

Helpers :: CheckerroRROR (:: addaccessallowedaceex (acl.m_ptr,

ACL_REVISION,

Inheritflags,

Generic_all,

UserSid.m_ptr));

Because ACLs are stored in a continuous memory cell, you need to assign a sufficiently large memory space to store ACL headers and all of it. The size of the assigned memory space is not important, enough to accommodate all items. After creating an ACL, you typically call the resource manager so that a copy of the ACL will be created. I use the AddaccessAllowedAceEx function to grant read permissions for each user, grant all permissions for users with tokens. Direct creation and editing more actual and more complex ACLs may be difficult, and it is easy to make mistakes. The main reason is related to the order of the ACE in the list, and when the access check is performed, it will be performed from topward in the list. If you add a negative ACE at the end of the list, and the ACE at the top of the list allows the user to access, even if this user access has been rejected, the access check can still be successful. Access checks take this taking a tie to improve efficiency. If you can sort the ACE, the program can run normally. What is more trouble is that Windows 2000 introduces a new model for implementing ACL inheritance. The function of the model is much better, but is particularly careful when implementing. The best suggestion I can do should never manipulate the ACL directly. The next part will tell you the method.

This article is taken from: http://www.microsoft.com/china/msdn/library/security/default.mspx? Mfr = true

转载请注明原文地址:https://www.9cbs.com/read-131834.html

New Post(0)