Windows 접근 제어

C++ 2011. 1. 11. 20:23
윈도우 접근 제어 관련

보안 디스크립터 : 객체에 관한 보안 정보가 저장되는 곳. (접근 허가/금지 등..)
소유자 SID, 프라이머리 그룹의 SID, ACL(DACL + SACL)를 갖는 구조
ACL (Access Control List)
ACE 구조체 배열로 DACL과 SACL를 갖음
SACL(System Access Control List)
시스템 접근 제어 리스트. 객체에 대한 접근 로그를 얻거나 접근시 관리자에게 통지하는 기능을 설정
DACL(Discretionary Access Control List) :
임의 접근 제어 리스트. 읽기, 쓰기 객체에 대한 접근 허가/금지를 트러스티(SID)마다 설정
DACL은 ACL 헤더와 ACE 리스트를 갖는데 각 ACE가 객체에 대해 트러스티에 의한
접근 허가/금지를 명시적으로 정의하는 기본 단위이며
전체 DACL이 대상 객체의 접근 권한 설정 전체를 기술함.

즉, DACL의 ACE는 다음 3가지 정보를 포함함.
1)접근 허가/금지의 플래그, 2)보호하는 접근 권한 종류를 나타내는 마스크 플래그, 3) 제어 대상의 트러스티 SID
SECURITY_DESCRIPTOR 구조체
: 보안 디스크립터의 실체인 구조체
SECURITY_ATTRIBUTES 구조체
: 보안 가능 객체 생성시 사용되며 SECURITY_DESCRIPTOR 구조체의 포인터를 갖음


User ID, Group ID에 대한 SID 및 정보 출력 샘플
#include <windows.h>
#include <tchar.h>
#include <sddl.h>
#include <iostream>

struct SIDTYPENAME
{
SID_NAME_USE type;
const TCHAR* name;
} nametbl[] = {
{SidTypeUser, _T("USER")},
{SidTypeGroup, _T("GROUP")},
{SidTypeDomain, _T("DOMAIN")},
{SidTypeAlias, _T("ALIAS")},
{SidTypeWellKnownGroup, _T("WELL KNOWN GROUP")},
{SidTypeDeletedAccount, _T("DELETED ACCOUNT")},
{SidTypeInvalid, _T("INVALID")},
{SidTypeComputer, _T("COMPUTER")},
{SidTypeUnknown, NULL}
};

void printSIDType(SID_NAME_USE use)
{
SIDTYPENAME* ptr = nametbl;
while (ptr->name != NULL)
{
if (use == ptr->type)
{
_tprintf(ptr->name);
return;
}
ptr++;
}
_tprintf(_T("UNKNOWN"));
}
int main(void)
{
setlocale(LC_ALL, "");

TCHAR sidBuf[512];
SID *pSid = (SID*)sidBuf;
DWORD sidLen = sizeof(sidBuf);
TCHAR domBuf[256];
LPTSTR domName = (LPTSTR)domBuf;
DWORD domLen = sizeof(domBuf);
SID_NAME_USE use;

//LookupAccountName : ID로 SID를 얻음
//LookupAccountSid  : SID로 ID를 얻음

//Guest 유저에 대한 SID 획득
if (LookupAccountName(NULL, _T("Guest"), pSid, &sidLen, domName, &domLen, &use)) 
{
LPTSTR sidString;
//ConvertSidToStringSid : 문자열 SID 획득
if (ConvertSidToStringSid(pSid, &sidString))
{
_tprintf(_T("SID : %s\n"), sidString);
_tprintf(_T("Domain : %s\n"), domName);
_tprintf(_T("Type : ")); printSIDType(use);
_tprintf(_T("\n"));
LocalFree(sidString);
}
}
return 0;
}


프로세스 접근 토큰에 설정된 기본 DACL 출력 프로그램 샘플
#include <windows.h>
#include <tchar.h>
#include <sddl.h>
#include <iostream>

struct SIDTYPENAME
{
SID_NAME_USE type;
const TCHAR* name;
} nametbl[] = {
{SidTypeUser, _T("USER")},
{SidTypeGroup, _T("GROUP")},
{SidTypeDomain, _T("DOMAIN")},
{SidTypeAlias, _T("ALIAS")},
{SidTypeWellKnownGroup, _T("WELL KNOWN GROUP")},
{SidTypeDeletedAccount, _T("DELETED ACCOUNT")},
{SidTypeInvalid, _T("INVALID")},
{SidTypeComputer, _T("COMPUTER")},
{SidTypeUnknown, NULL}
};

void printSIDType( SID_NAME_USE use)
{
SIDTYPENAME *ptr = nametbl;
while (ptr->name != NULL)
{
if (use == ptr->type)
{
_tprintf(ptr->name);
return;
}
ptr++;
}
_tprintf(_T("UNKNOWN"));
}

void printAccessMask(DWORD mask)
{
//접근 권한 종류
if( mask & GENERIC_READ) _tprintf(_T("GENERIC_READ "));
if( mask & GENERIC_WRITE) _tprintf(_T("GENERIC_WRITE "));
if( mask & GENERIC_EXECUTE) _tprintf(_T("GENERIC_EXECUTE "));
if( mask & GENERIC_ALL) _tprintf(_T("GENERIC_ALL "));

if( mask & SYNCHRONIZE) _tprintf(_T("SYNCHRONIZE "));
if( mask & WRITE_OWNER) _tprintf(_T("WRITE_OWNER "));
if( mask & WRITE_DAC) _tprintf(_T("WRITE_DAC "));
if( mask & READ_CONTROL) _tprintf(_T("READ_CONTROL "));
if( mask & DELETE) _tprintf(_T("DELETE "));
}

int main( void)
{
_tprintf(_T("--- Current Default DACL ---\n"));
HANDLE hToken;

//현재 프로세스(GetCurrentProcess())의 접근 토큰(access token)을 열어 토큰 핸들(hToken)을 얻음
if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
{
//간단하게 하기 위해 너무 긴 DACL은 미 처리
TCHAR buffer[1024];
TOKEN_DEFAULT_DACL* pDefdacl = (TOKEN_DEFAULT_DACL*)buffer; //DACL(discretionary access control list)
DWORD retlen = 0;

//토큰 핸들(hToken)을 통해 특정 타입의 접근 토큰(access token) 정보를 얻음.
//TokenDefaultDacl을 주어 DACL 전체를 얻어 pDefdacl에 담음
if (GetTokenInformation(hToken, TokenDefaultDacl,  pDefdacl, sizeof(buffer), &retlen))
{
if(pDefdacl->DefaultDacl != NULL) //DACL 획득에 성공했다면
{
int count = pDefdacl->DefaultDacl->AceCount; //DACL 안의 ACE 갯수를 얻음
_tprintf(_T("Number of ACEs: %d\n\n"), count);
ACE_HEADER *pHdr = (ACE_HEADER *)&pDefdacl->DefaultDacl[1]; //DACL안의 ACL 헤더를 얻음

for ( int i=0; i<count; i++) //DACL안의 ACE 갯수 만큼 LOOP
{
//ACE가 접근 허가 또는 금지를 사용할 수 있다면
if( pHdr->AceType == ACCESS_ALLOWED_ACE_TYPE || pHdr->AceType == ACCESS_DENIED_ACE_TYPE)
{
if( pHdr->AceType == ACCESS_ALLOWED_ACE_TYPE)
_tprintf(_T("ACE type: Allowed ACE \n")); //접근 허가 ACE
else
_tprintf(_T("ACE type: Denied  ACE \n")); //접근 금지 ACE

ACCESS_ALLOWED_ACE *pHdr2 = (ACCESS_ALLOWED_ACE *)pHdr;

//접근 권한 마스크 플래그의 의미 출력
_tprintf(_T("Access mask: ")); printAccessMask(pHdr2->Mask); _tprintf(_T("\n"));
//SID를 문자열로 출력
LPTSTR sidstr;
if(ConvertSidToStringSid((PSID)&pHdr2->SidStart, &sidstr))
{
_tprintf(_T("SID:  %s\n"), sidstr);
LocalFree( sidstr);
}
//SID에 대한 이름, 도메인, SID 타입 출력
TCHAR name[128], domain[128];
DWORD namelen = sizeof(name);
DWORD domlen = sizeof(domain);
SID_NAME_USE use;
if (LookupAccountSid(NULL, (PSID)&pHdr2->SidStart, name, &namelen, domain, &domlen, &use))
{
_tprintf(_T("name  : %s\n"), name);
_tprintf(_T("domain: %s\n"), domain);
_tprintf(_T("type  : ")); printSIDType(use); _tprintf(_T("\n"));
}
else
{
_tprintf(_T("Cannot print a name and a type.\n"));
}
}
else //접근이나 금지 ACE가 아닌 경우
{
_tprintf(_T("Other types of ACE\n"));
}
_tprintf(_T("\n"));
//다음 ACE를 가르키도록 주소 이동
pHdr = (ACE_HEADER *)((char *)pHdr + pHdr->AceSize);
}
} else { //DACL이 아닌 경우
_tprintf(_T("there's no default dacl.\n"));
}
} else {
_tprintf(_T("GetTokenInformation() error: %d.\n"), GetLastError());
}
CloseHandle(hToken);
} else{
_tprintf(_T("OpenProcessToken() error: %d.\n"), GetLastError());
}

return 0;
}


명시적 제어 설정 부분 예제 코드
/*yamoe 사용자에 대해서만 읽기 접근 허가*/
//SECURITY_DESCRIPTOR 구조체 초기화
SECURITY_DESCRIPTO sdesc;
InitializeSecurityDescriptor(&sdesc, SECURITY_DESCRIPTOR_REVISION);
//ACE 엔트리 생성
EXPLICIT_ACCESS ea;
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = 0;
ea.Trustee.ptstrName = "yamoe"
BuildTrusteeWithName(&ea.Trustee, ea.Trustee.ptstrName);
//하나의 ACE를 포함한 ACL 신규 생성
PACL *pAcl;
SetEntriesInAcl(1, $ea, NULL, &pAcl);
//ACL을 보안 디스크립터에 설정
SetSecurityDescriptorDacl(&sdesc, TRUE, pAcl, TRUE);



/*빈 DACL 설정 : 완전 접근 금지*/
//SECURITY_DESCRIPTOR 구조체 초기화
SECURITY_DESCRIPTO sdesc;
InitializeSecurityDescriptor(&sdesc, SECURITY_DESCRIPTOR_REVISION);
//빈 ACL 생성
ACL acl;
InitializeAcl(&acl, sizeof(acl), ACL_REVISION);
//ACL을 보안 디스크립터에 설정
SetSecurityDescriptorDacl(&sdesc, TRUE, &acl, TRUE);


/*NULL DACL 설정 : 완전 접근 허가*/
//SECURITY_DESCRIPTOR 구조체 초기화
SECURITY_DESCRIPTO sdesc;
InitializeSecurityDescriptor(&sdesc, SECURITY_DESCRIPTOR_REVISION);

//ACL 포인터에 NULL 지정
//두번째 인자에 TRUE를 넘기는 것이 핵심 사항
SetSecurityDescriptorDacl(&sdesc, TRUE, NULL, TRUE);




프로그램 설치할때 모든 사용자 혹은 특정 사용자에게만 실행 권한 줄때 쓰이는 건가.. 실제 사용 예는 무엇인가?