3.1. 프로세스

유닉스 계열 시스템에서 사용자는 프로세스를 실행시킴으로써 작업을 한다. 대부분의 유닉스 시스템은 별개의 개념으로서 ``쓰레드"를 지원하는데 쓰레드는 프로세스내에서 메모리를 공유하며 시스템 스케쥴러가 실제로 이를 스케쥴한다. 리눅스에서는 이를 다르게 다루는데 (필자 의견으로는 더욱 나은 접근 방법을 사용한다) 쓰레드와 프로세스사이에 본질적인 차이는 없다. 리눅스에서는 대신 프로세스가 다른 프로세스를 생성할 때 어떠 자원 (예를 들면 메모리) 이 공유될 지를 선택할 수 있다. 곧이어 커널은 쓰레드 수준의 속도를 얻기 위해 최적화를 수행하는데 더욱 자세한 정보는 clone(2) 를 보라. 리눅스 커널 개발자들은 ``쓰레드" 또는 ``프로세스"라기 보다는 ``태스크"를 사용하는 반면 형식적인 문서에서는 프로세스를 사용하는 경향이 있음을 언급하는 것은 가치가 있다 (따라서 저자는 ``프로세스"란 용어를 사용할 것이다). 다중 쓰레드 애플리케이션을 프로그래밍할때는 보통 이러한 차이점을 드러내지 않는 표준 쓰레드 라이브러리중 하나를 사용하는 것이 더욱 좋다. 이는 대개 쓰레드의 이식성을 더욱 높이지만 단독 운영 체제 쓰레드로 한 애플리케이션 수준 이상의 쓰레드를 구현함으로써 어떤 라이브러리는 추가적인 간접 (indirection) 수준을 제공한다; 이는 어떤 애플리케이션에 대해 몇몇 시스템에서 상당히 향상된 성능을 제공할 수 있다.

3.1.1. 프로세스 속성

유닉스 계열 시스템에서 각 프로세스와 관련된 대표적인 속성은 다음과 같다:

다음은 프로세스와 관련해 덜 공통된 속성들이다:

리눅스에서 실제로 각 프로세스에 어떤 속성들이 관련되었는 지를 정확히 알 필요가 있다면 리눅스 소스 코드가 가장 명확한 소스이다. 특히 /usr/include/linux/sched.h 파일에서 task_struct 의 정의를 참조해라.

새로운 프로세스를 생성하는 이식성있는 방식으로 리눅스는 fork(2) 호출을 사용한다. BSD 는 최적화 기법으로 변형된 vfork(2) 를 도입하였다. vfork 과 관련된 최종 결과는 간단한데 이를 피할 수 있다면 사용하지 마라. 더욱 자세한 정보는 7.5절 를 참조해라.

리눅스는 고유의 clone(2) 호출을 지원하는데 fork(2) 호출과 유사하게 작동하지만 공유되어야 하는 자원 (예, 메모리, 파일기술자 등) 을 지정할 수 있다. 여러 가지 BSD 시스템들은 원래 Plan9 에서 개발된 rfork() 시스템 호출을 구현하는데 동일한 일반적 개념을 갖고 있지만 상이한 의미 체계 (semantics) 를 갖고 있다 (이는 공유되는 것에 대해 보다 엄격한 제어를 갖는 프로세스를 생성한다). 이식성있는 프로그램이라면 가능한 이러한 호출을 직접적으로 사용하지 않아야 한다; 앞에서 언급했듯이 프로그램들은 쓰레드를 구현하기 위해 이러한 호출을 사용하는 쓰레딩 라이브러리에 의존해야 한다.

이 책은 프로그램 작성에 대한 완전한 지침서가 아니며 따라서 프로세스를 다루는 널리 얻을 수 있는 정보를 건너뛸 것이다. 더욱 자세한 정보는 wait(2), exit(2) 등에 대한 문서를 봐야 한다.

3.1.2. POSIX Capabilities

POSIX 능력 (capability) 은 일반적으로 루트가 소유하는 권한을 보다 구체적인 권한들의 커다란 셋으로 분할할 수 있도록 하는 비트들의 셋이다. 이는 IEEE 표준 초안에 의해 정의되는데 리눅스 고유의 것은 아니며 또한 일반적으로 다른 유닉스 계열 시스템에 의해 지원되지도 않는다. 리눅스 커널 2.0 은 POSIX 능력을 지원하지 않는 반면 2.2 버전은 프로세스에 대한 이 지원을 추가하였다. 이 문서를 포함하여 리눅스 문서들이 ``루트 권한을 필요로 한다" 라고 할 때 거의 모든 경우에 있어 이는 능력 문서에 인용된 바와 같이 ``능력을 필요로 한다"를 의미한다. 필요한 특정 능력을 알고 싶다면 능력 문서에서 이를 조사해라.

리눅스에서 궁극적인 목적은 파일시스템내의 파일에 능력이 결부될 수 있도록 하려는 것인데 그러나 이 글을 쓰는 시점에서 이는 아직까지 지원되지 않고 있다. 능력 양도에 대한 지원도 있지만 디폴트로 이러한 기능은 금지되어 있다. 리눅스 2.2.11 버전은 ``능력 제한 셋 (capability bounding set)" 라는 능력을 보다 직접적으로 사용할 수 있게 하는 특징을 추가하였다. 능력 제한 셋은 시스템내의 모든 프로세스가 보유할 수 있는 능력 목록이다 (물론 그렇지않으면 특별한 init 프로세스 만이 이를 보유할 수 있다). 능력이 제한 셋에 없다면 어떤 프로세스라도 지닌 권한에 상관없이 이를 사용하지 못할 것이다. 이 특징은 예를 들어 커널 모듈 적재를 금지하는데 사용될 수 있는데 이를 이용한 예제 도구는 http://pweb.netcom.com/~spoon/lcap/ 의 LCAP 이다.

POSIX 능력에 대한 더욱 자세한 정보는 ftp://linux.kernel.org/pub/linux/libs/security/linux-privs 에서 얻을 수 있다.

3.1.3. 프로세스 생성과 조작

프로세스는 fork(2), vfork(2) (추천하지 않는다) 또는 리눅스 고유의 clone(2) 를 사용하여 생성될 수 있는데 이러한 시스템 호출 모두는 기존 프로세스로부터 두 프로세스를 생성함으로써 기존 프로세스를 복제한다. 프로세스는 execve(2) 또는 이에 대한 다양한 전위 (front-end) (예를 들어 exec(3), system(3) 및 popen(3) 를 보라) 를 호출함으로써 다른 프로그램을 실행시킬 수 있다.

프로그램이 실행되어 그 파일의 setuid 또는 setgid 비트 셋이 설정될 때 프로세스의 EUID 또는 EGID 각각은 대개 파일의 값들로 설정된다. 이 기능성은 경쟁 상태 (race condition) 때문에 setuid 또는 setgid 스크립트를 지원하기 위해 사용될 때 초기 유닉스의 보안 결함의 원인이였다. 커널이 어떤 인터프리터를 실행시켜야 하는지 알기 위해 파일을 여는 시간과 방금 id 가 설정된 인터프리터가 실행되어 파일을 해석하기 위해 이를 다시 여는 시간사이에 해커가 직접적으로 또는 심볼릭 링크를 통해 파일을 변경할 수 있다.

여러가지 유닉스 계열 시스템들은 다른 방식으로 setuid 스크립트에 대한 보안 쟁점을 다룬다. 리눅스와 같은 시스템들은 스크립트를 실행할 때 setuid 와 setgid 비트를 완전히 무시하는데 이는 명백히 안전한 접근 방법이다. 대부분의 SysVr4 와 BSD 4.4 현재 버전은 커널 경쟁 상태를 피하기 위해 다른 접근 방법을 사용하는데, 커널이 열려고 하는 set-id 스크립트의 이름을 인터프리터에 전달할 때 경쟁 상태를 허용할 수 있는 경로이름보다는 대신 파일이름 /dev/fd/3 을 전달한다. 이는 스크립트상에 이미 열려진 특별 파일로 따라서 공격자가 악용할 수 있는 경쟁 상태는 없다. 이러한 파일시스템에서도 저자는 밑부분에 논의될 것이지만 보안적인 프로그램 작성에 setuid/setgid 쉘 스크립트 언어를 사용하지 말라고 추천한다.

어떤 경우에 있어 프로세스는 여러 가지 UID 와 GID 값들에 영향을 미칠 수 있는데 setuid(2), seteuid(2), setreuid(2) 와 리눅스 고유의 setfsuid(2) 를 보라. 특히, 유보된 사용자 id (SUID) 속성은 신뢰된 프로그램이 일시적으로 UID 들을 변경할 수 있도록 하는 것이다. SUID 를 지원하는 유닉스 계열 시스템들은 다음 규칙을 사용한다: RUID 가 변경되거나 또는 EUID 가 RUID 와 동일하게 설정되지 않으면 SUID 가 새로운 EUID 로 설정된다. 권한이 없는 사용자들은 그들의 SUID 로부터 EUID 를 설정할 수 있으며 RUID 를 EUID 로 EUID 를 RUID 로 설정할 수 있다.

리눅스 고유의 FSUID 프로세스 속성은 NFS 서버와 같은 프로그램들에게 시그널을 프로세스에 보내기 위한 UID 허가권을 주지 않고서 이들이 자신들을 어떤 주어진 UID 권한을 갖는 파일시스템으로 제한할 수 있도록 하는 속성이다. EUID 가 변경될 때마다 FSUID 는 새로운 EUID 값으로 변경된다; FSUID 값은 리눅스 고유 호출인 setfsuid(2) 를 사용하여 별도로 설정될 수 있다. 루트이외의 호출자들은 FSUID 를 단지 현재 RUID, EUID, SEUID 또는 현재 FSUID 값으로만 설정할 수 있음을 주목해라.