25 사용자와 그룹

시스템에서 로그인하고 있는 모든 사용자는 사용자 ID(user ID)라고 불리는 한 개의 단일한 숫자에 의해서 구분된다. 각 프로세스는 그 프로세스를 가진 사용자의 억세스 권한을 알리는 유효 사용자 ID를 갖는다. 사용자들은 억세스 제어를 목적으로 하여 그룹으로 분류된다. 각 프로세스는 그 프로세스가 파일들을 억세스하기 위해서 사용할 수 있는 어떤 그룹들이 있는지를 알리는 한 개 이상의 그룹 ID(group ID)가 있다. 프로세스의 유효 사용자 ID 와 유효 그룹 ID들은 persona의 집합적인 형식이다. 이것은 그 프로세스가 어떤 파일을 억세스 할 수 있는지를 결정한다. 일반적으로, 프로세스는 부모 프로세스로 부터 persona를 상속받지만, 특별한 상황하에서는 그 persona를 변경해서 억세스 권한을 변경할 수 있다.

시스템에 있는 각 파일은 또한 사용자 ID 와 그룹 ID를 갖는다. 억세스제어(access control)는, 실행되고 있는 프로세스의 사용자 ID 와 그룹 ID를 파일의 사용자 ID 와 그룹 ID를 비교함으로써 수행된다. 그 시스템은 등록된 사용자들을 모두 데이터베이스에 저장하고, 다른 데이터베이스에 정의된 모든 그룹들을 저장한다. 그 데이터베이스를 시험하기 위해 사용할 수 있는 라이브러리 함수들이 있다.


25. 1 사용자와 그룹 ID들

컴퓨터 시스템에 등록된 각 사용자들은 사용자 이름(user name) 과 사용자ID(user ID) 에 의해 구분된다. 일반적으로, 각 사용자 이름은 한 개의 단일한 사용자 ID를 갖지만, 같은 사용자 ID에 여러개의 로그인 이름들을 가질 수 있다. 사용자 이름과 그에 해당하는 사용자 ID는 25. 12절 [User Database] 에 설명된, 당신이 억세스 할 수 있는 데이터베이스에 저장되어 있다.

사용자는 그룹 안에 분류된다. 각 사용자 이름은 디폴트로는 한 개의 그룹이지만, 한 개 이상의 그룹에 소속된다. 같은 그룹의 멤버인 사용자들은 그 그룹의 멤버가 아닌 사용자가 억세스할 수 없는 자원들(파일과 같은)을 공유한다. 각 그룹은 한 개의 그룹 이름과 그룹 ID를 갖는다. 25. 13절[Group Database] 에, 그룹 ID 또는 그룹 이름에 대한 정보를 어떻게 찾는지 나와있다.


25. 2 프로세스의 persona

어느 시간에, 각 프로세스는 프로세스의 권한을 결정하는 한 개가 단일한 사용자 ID와 한 개의 그룹 ID를 갖는다. 그들은 프로세스의 집합적인 persona라고 불리는데, 왜냐하면 그들은 억세스 제어의 목적을 위하여 "그것은 누구이다"를 결정하기 때문이다. 그 ID들은 프로세스의 유효 사용자 ID(effective user ID) 와 유효 그룹 ID(effective group ID) 라고 불린다. 당신의 로그인 쉘은 당신의 사용자 ID 와 당신의 디폴트 그룹 ID로 구성되는 persona로 시작한다. 일반적인 환경에서, 모든 당신의 다른 프로세스들은 그들의 값들을 상속받는다.

프로세스는 그 프로세스를 만들었던 사용자를 확인할 수 있는 실제사용자 ID(real user ID)와 사용자의 디폴트 그룹을 확인하는 실제 그룹 ID(real group ID)를 갖는다. 그들의 값들은 억세스 제어의 역할을 하지 않기 때문에, 우리는 그들을 persona의 부분으로 간주하지 않는다. 하지만 그들은 중요하다. 실제 와 유효 사용자 ID 둘은 모두 프로세스의 생존기간 동안에 변경될 수 있다. 25. 3절 [Why Change Persona] 참조. 부가적으로, 사용자는 여러 개의 그룹에 포함될 수 있기 때문에, persona에는 억세스 권한에 기여하는 부가적인 그룹 ID들이 포함된다. 어떻게 프로세스의 유효 사용자 ID와 그룹 ID들이 파일을 접근하는 권한에 영향을 미치는지에 대한 상세한 정보들은, 9. 8. 6절 [Access Permission] 참조.

프로세스의 사용자 ID는 kill 함수를 사용해서 시그널들을 보낼 수 있는 권한 또한 제어한다. 21. 6. 2절 [Signaling Another Process] 참조.


25. 3 왜 프로세스의 persona를 변경하는가

프로세스를 위하여 사용자 그리고/또는 그룹 ID들을 변경할 필요가 있는 가장 명백한 상황이 있는 곳은 로그인 프로그램이다. 로그인이 실행을 시작할 때, 그 사용자 ID는 루트이다. 작업은 로그인하고 있는 사용자의 사용자 ID와 그룹 ID들을 가진 쉘을 시작하는 것이다. (이것을 완전히 수행하기 위해서, 로그인은 persona 뿐만 아니라 실제 사용자와 그룹 ID들을 설정해야만 한다. 하지만 이것은 특별한 경우이다. ) persona를 변경하는 더 일반적인 경우는 보통의 사용자 프로그램에서 실제로 그 프로그램을 실행시키고 있는사용자에게는 억세스가 가능하지 않는 자원을 사용자가 억세스할 필요가 있을 때이다.

예를 들어, 당신이 당신의 프로그램에 의해 제어되지만 다른 사용자에 의해서 직접적으로 읽거나 갱신될 수 없는 어떤 파일이 있다고 했을 때, 다른 사용자에게는 개방되지 않은 어떤 종류의 프로토콜의 기능을 원하거나, 또는 그것이 가지고 있는 정보의 비밀이나 완전을 보장하기 원할 때, persona를 변경하는 것이 필요하다. 이 제한된 억세스의 종류는 원하는 자원과 매치되도록 유효 사용자와 그룹 ID를 프로그램이 변경함으로써 가능하다.

그러면, 파일안에 점수를 저장하는 게임 프로그램을 상상해보자. 게임프로그램 자체는 누가 그것을 실행시키는지에 상관없이 이 파일을 갱신할 수 있도록 할 필요가 있지만, 만일 사용자가 그 게임을 실행시키지 않고도 그 점수파일에 기록이 가능하다면, 그들은 그들이 좋아하는 어느 점수로 그 점수파일에 그들 점수를 기록할 수 있다. 어떤 사람은 이것을 상상할 수도 없거나, 아니면 비난할 것이다. 이러한 것은 점수 파일을 소유하는 새로운 사용자 ID와 로그인 이름을 만들고 이 사용자에 의해서만 오직 기록 가능하도록 그 파일을 만들면 방지할 수 있다. 그러면, 게임 프로그램이 이 파일을 갱신하기 원할 때, 그것은 그 게임의 유효 사용자 ID를 변경할 수 있다. 실제로, 프로그램은 점수파일에 기록할 수 있는 게임의 persona를 받아 들여야만 한다.


25. 4 어떻게 응용프로그램이 persona를 변경할 수 있는가

프로세스의 persoan을 변경하기 위한 능력은 무심한 비밀의 침해나, 또는 고의적인 남용의 소스(source)가 될 수 있다. persona를 변경하는, 프로그램에 포함된 문제는 특별한 상황들에 제한이 있기 때문이다.

당신은 제멋대로 당신이 원한다고 당신의 사용자 ID 나 그룹 ID를 변경 할 수는 없다; 오직 특권이 부여된 프로세스들만이 그것을 할 수 있다. 대신에, 프로그램을 위하여 persona를 변경하기 위한 일반적인 방법은 미리 특별한 사용자나 그룹으로 변경하여 프로그램이 시작되는 것이다. 이것은 파일의 억세스 모드의 setuid 와 setgid 비트들의 함수이다. 9. 8. 5절 [Permission Bits] 참조.

억세스 가능한 파일의 setuid 비트가 설정될 때, 그 파일은 자동적으로 그 파일을 소유한 사용자로 유효 사용자 ID를 변경하여 실행된다. 그와 같이, setgid 비트가 설정된 파일은 파일의 그룹으로 유효사용자 ID를 변경하여 실행된다. 23. 5절 [Executing a File] 참조. 특별한 사용자나 그룹 ID로 변경하여 만들어진 파일은 사용자나 그룹 ID에게 완전한 억세스를 보장한다. 파일 모드와 억세스가능성에 대한 일반적인 내용은 9. 8절 [File Attributes] 참조하라.

프로세스는 유효 사용자(또는 그룹) ID는 실제 ID로 언제든지 다시 변경할 수 있다. 프로그램을 좀더 견고한 프로그램으로 만들기 위해서, 그들이 필요치 않을 때는 그들에 대한 특별한 권한을 없애야(turn off) 한다.


25. 5 프로세스의 persona 읽기

다음은 실제와 유효 사용자와 그룹 ID들을 읽기 위한 함수들의 상세한 설명이다. 그 기능들을 사용하기 위해서는 당신은 헤더파일 `sys/types. h'와 `unistd. h'를 포함시켜야만 한다.

데이터타입 : uid__t

이것은 사용자 ID들을 나타내는데 사용되는 정수 데이터 타입이다. GNU 라이브러리에서, 이것은 unsigned int 와 같다.

데이터타입 : gid__t

이것은 그룹 ID들을 나타내기 위해서 사용되는 정수 데이터타입이다. GNU 라이브러리에서, 이것은 unsigned int 와 같다.

함수 : uid_t getuid (void)

getuid 함수는 프로세스의 실제 사용자 ID를 반환한다.

함수 : gid_t getgid (void)

getgid 함수는 프로세스의 실제 그룹 ID들을 반환한다.

함수 : uid_t geteuid (void)

geteuid 함수는 프로세스의 유효 사용자 ID를 반환한다.

함수 : gid_t getegid (void)

getegid 함수는 프로세스의 유효 사용자 ID 를 반환한다.

함수 : int getgroups (int count, gid_t *groups)

getgroups 함수는 프로세스의 부가적인 그룹 ID들에 대해서 묻는데 사용된다. 그들 그룹 ID들의 개수까지도 배열 groups에 저장된다; 함수로부터의 반환값은 실제로 저장된 그룹 ID들의 개수이다. 만일 count가 부가적인 그룹 ID들의 전체 개수보다 작다면, getgroups는 -1의 값을 반환하고 errno를 EINVAL로 설정한다. 만일 count 가 0이면, getgroups는 단지 부가적인(supplementary) 그룹 ID들의 전체 개수를 반환한다. 부가적인 그룹들을 지원하지 않는 시스템에서는, 이것은 항상 0이 될 것이다.
다음은 getgroups가 어떻게 모든 부가적인 그룹 ID들을 읽어서 사용하는지에 대한 것이다.
gid_t *read_all_groups (void)
{
int ngroups = getgroups (NULL, 0);
gid_t *groups = (gid_t *) xmalloc (ngroups * sizeof (gid_t));
int val = getgroups (ngroups, groups);
if (val < 0) {
free (groups);
return NULL;
}
return groups;
}


25. 6 사용자 ID 설정하기

이 절은 프로세스의 사용자 ID(실제 그리고/또는 유효)를 변경하기 위한 함수들을 설명하고 있다. 그 기능들을 사용하기 위해서, 당신은 헤더파일 `sys/types. h'와 `unistd. h'를 포함해야만 한다.

함수 : int setuid (uid_t newuid)

이 함수는 적당한 특권을 가지고 제공된 프로세스에서, 그 프로세스의 실제 사용자 ID 와 유효사용자 ID를 newuid로 설정한다. 만일 프로세스가 특권이 없다면, newuid는 실제 사용자 ID또는 저장된 사용자 ID(만일 시스템이 _POSIX_SAVED_IDS 를 지원한다면)중의 하나가 되어야만 한다. 이 경우, setuid는 오직 유효 사용자 ID만을 설정하고 실제 사용자 ID는 설정하지 않는다.
setuid 함수는 성공하면 0의 값을 반환하고 에러가 발생하면 -1의 값을 반환한다.  
다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EINVAL : newuid 인수의 값이 유용하지 않다.

EPERM

그 프로세스가 적당한 특권을 가지지 않는다; 당신은 정해진 ID로 변경하기 위한 권한을 가지지 않았다.

함수 : int setreuid (uid_t ruid, uid_t euid)

이 함수는 ruid로 프로세스의 실제 사용자 ID를 설정하고 euid로 유효 사용자 ID를 설정한다. 만일 ruid가 -1이면, 그것은 실제 사용자 ID를 변경하지 않음을 의미한다; 그처럼 만일 euid가 -1이면 유효사용자 ID를 변경하지 않음을 의미한다.
setreuid 함수는 저장된 ID(saved IDs)들을 지원하지 않는 4. 3 BSD 유닉스와의 호환성을 위해서 존재한다. 당신은 프로세스의 유효와 실제 사용자 ID들을 교환하기 위해서 이 함수를 사용할 수 있다. (특권을 가진 프로세스는 이 특별한 사용에 대해서 제한이 없다. ) 만일 저장된 ID들이 지원된다면, 당신은 이 함수대신에 25. 8절 [Enable/Disable Setuid] 에 있는 함수들을 사용하는 것이 좋다.
반환값은 성공하면 0이고 실패하면 -1이다.
다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EPERM

그 프로세스가 적당한 특권을 가지지 않았다; 당신은 정해진 ID로 변경하기 위한 권한이 없다.


25. 7 그룹 ID들을 설정하기

이 절은 프로세스의 그룹 ID들(실제 와 유효)을 변경하기 위한 함수들을 설명하고 있다. 이 기능들을 사용하기 위해서, 헤더파일 `sys/types. h'와 `unistd. h'를 포함해야만 한다.

함수 : int setgid (gid_t newgid)

이 함수는 적당한 특권을 가지고 제공된 프로세스의 실제와 유효 그룹 ID를 newgid로 설정한다. 만일 그 프로세스가 특권이 없다면, newgid는 반드시 실제 그룹 ID 또는 저장된 그룹 ID중의 하나와 동일해야만 한다. 이 경우, setgid는 오직 유효 그룹 ID만 설정하고 실제 그룹 ID는 설정하지 않는다. setgid를 위한 반환값과 에러 상황들은 setuid와 같다.

함수 : int setregid (gid_t rgid, fid_t egid)

이 함수는 rgid로 프로세스의 실제 그룹 ID 를 설정하고 egid로 유효 그룹 ID를 설정한다. 만일 rgid가 -1이면, 그것은 실제 그룹 ID를 변경하지 않음을 의미한다; 그와 같이 만일 egid가 -1이면, 유효 그룹 ID를 변경하지 않음을 의미한다.
setregid 함수는 저장된 ID들을 지원하지 않는, 4. 3 BSD 유닉스와의 호환성을 위해서 제공되었다. 당신은 프로세스의 유효와 실제 그룹 ID들을 교환하기 위해서 이 함수를 사용할 수 있다( 특권을 가진 프로세스들은 이 사용에 대해서 제한이 없다. ). 만일 저장된 ID들이 지원된다면, 이 함수를 사용하는 대신에 25. 8절 [Enable/Disable Setuid] 에 있는 함수들을 사용하라. setregid 를 위한 반환값과 에러상황들은 setreuid와 같다. GNU 시스템은 특권을 가진 프로세스가 그들의 부가적인 그룹 ID들을 변경하는 것을 허용한다. setgroups 나 initgroups를 사용하기 위해서, 당신은 헤더파일 `grp. h'를 포함해야만 한다.

함수 : int setgroups (size_t count, gid_t *groups)

이 함수는 프로세스의 부가적인 그룹 ID들을 설정한다. 그것은 오직 특권을 가진 프로세스로 부터 호출될 수 있다. count 인수는 배열 groups안에 있는 그룹 ID들의 개수를 지정한다.
이 함수는 성공하면 0을 반환하고 에러가 발생하면 -1을 반환한다. 다음의 errno 는 이 함수를 위해 정의된 에러상황이다.
EPERM : 호출한 프로세스가 특권이 없다.

함수 : int initgroups (const char *user, gid_t gid)

initgroups 함수는 사용자 이름 user를 위해서, 디폴트가 되도록 프로세스의 부가적인 그룹 ID들을 설정하기 위해서 setgroups를 호출한다. 그룹 ID gid 또한 포함된다.


25. 8 Setuid 억세스를 가능하게 하거나 불가능하게 하기

전형적인 setuid를 사용하는 프로그램에서 항상 특별한 억세스가 필요한 것은 아니다. 그래서 필요하지 않을 때 이 억세스를 꺼버리면, 무의식적인 억세스에 대한 빌미를 제공하지 않게된다. 만일 시스템이 저장된 사용자 ID 기능을 지원하지 않는다면, 당신은 setuid로 이것을 수행할 수 있다. 게임 프로그램이 시작할 때, 그것의 실제 사용자 ID는 jdoe이고, 유효 사용자 ID는 games이고, 그것의 저장된 사용자 ID 또한 games이다. 그 프로그램은 다음과 같이, 시작할 때 일단 사용자 ID값들을 기록하게 된다.

    user_user_id = getuid ();

    game_user_id = geteuid ();

그러면 setuid (user_user_id); 로 게임 파일 억세스를 끌 수 있고 setuid (game_user_id); 로 다시 켤 수 있다. 이 프로세스를 통해서, 실제 사용자 ID를 jdoe로 남고 저장된 사용자 ID는 games로 저장되기 때문에, 프로그램은 항상 다른 것으로 유효 사용자 ID를 설정할 수 있다.

저장된 사용자 ID 기능을 지원하지 않는 다른 시스템에서, 당신은 프로세스의 실제 사용자 ID 와 유효 사용자 ID를 교환하기 위해서는 setreuid를 사용해서 setuid 억세스를 켜고 끌 수 있다. 다음처럼;

    setreuid (geteuid (), getuid ());

이것은 실패하지 않고_항상 허용되는 특별한 경우이다.

왜 이것은 setuid 억세스를 토글링하는 효과를 갖는가? 게임 프로그램이 단지 시작되어 있다고 가정하고, 유효 사용자 ID가 games 일 동안 실제 사용자 ID는 jdoe라고 가정하자. 이 경우, 게임은 점수 파일을 기록할 수 있다. 만일 두 개의 유효 사용자 ID 와 실제 사용자 ID를 교환한다면, 실제 사용자 ID는 games가 되고 유효 사용자 ID는 jdoe가 된다; 이제 프로그램은 오직 jdoe만 억세스를 갖는다. 다른 교환은 유효 사용자 ID를 다시 games로 되돌리고 점수 파일에 억세스를 저장한다.

시스템의 두 종류를 다루기 위해서는, 다음처럼 프리프로세서를 사용해서 저장된 사용자 ID 기능을 지원하는지 테스트해 보아야 한다.

#ifdef _POSIX_SAVED_IDS
setuid (user_user_id);
#else
setreuid (geteuid (), getuid ());
#endif


25. 9 setuid 프로그램 예제

다음은 유효 사용자 ID를 변경하는 프로그램을 어떻게 준비하는지 보여주는 예제이다. 이것은 caber-toss라고 불리는 게임 프로그램의 일부분으로써 그 게임 프로그램은 오직 게임 프로그램 그 자체에 의해서만 점수 파일을 다룰 수 있도록 되어있다. 그 게임 프로그램은 실행파일이 set-user-ID 비트로 인스톨될 것이고 실행파일이 점수파일과 같은 사용자에 의해 소유된다고 가정한다. 전형적으로, 시스템 관리자는 이 목적을 위해서 게임과 같은 계정을 준비할 것이다.

실행가능파일은 모드 4755가 주어지고, 그래서 `ls -l'을 하면 다음과 같은 출력을 얻게된다.

-rwsr-xr-x 1 games 184422 Jul 30 15: 17 caber-toss

set-user-ID 비트는 파일 모드에서 `s'로 나타난다. 점수 파일을 모드 644로 주어지고, `ls -l'하면 다음과 같은 결과를 얻는다.

다음은 어떻게 변경된 사용자 ID를 준비할 것인지를 보여주는 프로그램의 일부분이다. 이 프로그램은 만일 시스템이 저장된 ID 기능을 지원한다면 그것을 사용하고, 그렇지 않다면 setreuid를 사용해서 유효 사용자 ID와 실제 사용자 ID를 교환한다.

#include <stdio. h>
#include <sys/types. h>
#include <unistd. h>
#include <stdlib. h>
/* 유효와 실제 사용자 ID들을 저장하라. */
static uid_t euid, ruid;
/* 원래의 값으로 유효 사용자 ID를 반환하라. */
void
do_setuid (void)
{
int status;
#ifdef _POSIX_SAVED_IDS
status = setuid (euid);
#else
status = setreuid (ruid, euid);
#endif
if (status < 0) {
fprintf (stderr, "Couldn't set uid. \n");
exit (status);
}
}
/* 실제 사용자 ID로 유효 사용자 ID를 설정하라. */
void
undo_setuid (void)
{
int status;
#ifdef _POSIX_SAVED_IDS
status = setuid (ruid);
#else
status = setreuid (euid, ruid);
#endif
if (status < 0) {
fprintf (stderr, "Couldn't set uid. \n");
exit (status);
}
}
/* 메인 프로그램 */
int
main (void)
{
/* 실제와 유효 사용자 ID들을 저장하라. */
ruid = getuid ();
euid = geteuid ();
undo_setuid ();
/* 게임을 하고 점수를 기록하라. */
. . .
}
메인 함수에서 첫째로 어떻게 실제 사용자 ID로 유효 사용자 ID를 되돌려 설정하는지에 대해서 주목하라. 이것은 사용자가 게임을 실행하고 있을 동안 수행되는 파일 억세스들로써 권한을 결정하기 위해서 실제 사용자 ID를 사용한다. 오직 프로그램이 점수 파일을 개방할 필요만 있을 때 다음처럼, 원래의 유효 사용자 ID로 되바꾼다.
/* 점수를 기록하라. */
int
record_score (int score)
{
FILE *stream;
char *myname;
/* 점수파일을 개방하라. */
do_setuid ();
stream = fopen (SCORES_FILE, "a");
undo_setuid ();
/* 파일에 그 점수를 기록하라. */
if (stream)
{
myname = cuserid (NULL);
if (score < 0)
fprintf(stream, "%10s: Couldn't lift the caber. \n", myname);
else
fprintf (stream, "%10s: %d feet. \n", myname, score);
fclose (stream);
return 0;
} else
return -1;
}


25. 10 setuid 프로그램을 만들기 위한 팁

setuid 프로그램은 원래 의도하지 않았던 사용자 억세스를 부여하는 오류를 범하기 쉽다_실제로, 만일 당신이 이러한 문제를 피하기를 원한다면, 각별한 주의를 해야만 한다. 다음은 의도되지 않은 억세스를 막고, 만일 그것일 발생했을 때 그 영향력을 최소화하기 위한 안내지침이다.

절대적인 필요가 없는 한 root 와 같은 특권을 가진 사용자 ID들은 setuid 프로그램을 가져서는 안된다. 만일 자원(resource)이 당신의 특정한 프로그램에게 특정한 것이라면, 특권을 가지지 않은 사용자 ID 또는 그룹 ID가 단지 그 자원을 다루기만 하도록, 새롭게 정의하는 것이 좋다.

유효 사용자 ID를 변경하는것과 함께 system 함수와 exec 함수를 사용하는 것에 대해서는 주의를 해야만 한다. 당신 프로그램의 사용자가 변경된 사용자 ID를 사용하여 프로그램을 실행시키는 것을 허가하지 말아라. execlp 와 execvp 함수들도 위험을 내포하고 있다 (그들은 사용자의 PATH 환경변수에 의존하여 실행되기 때문이다. ) 만일 변경된 ID로 다른 프로그램을 실행해야만 한다면, 실행가능 파일을 절대 파일 이름( 6. 2. 2절 [File Name Resolution] 참조. ) 어로 지정하고, 실행가능 파일과 원래의 users 와 같은 모든 포함된 디렉토리들이 다른 프로그램과 함께 그것의 위치를 변경하지 못하도록 보호를 해야만 한다.

실제로 자원을 사용하는 프로그램의 일부분에서 자원을 제어하고 있는 사용자 ID만을 사용하라. 당신이 그 작업을 마쳤을 때, 실제 사용자의 사용자 ID로 유효 사용자 ID를 반환하라. 25. 8절 [Enable/Disable Setuid] 참조.

만일 당신의 프로그램에서 setuid 부분이 제어할 수 있는 자원이외에 다른 파일을 억세스할 필요가 있다면, 그와같은 파일을 억세스할 수 있는 권한을 가진 실제 사용자를 조회해야만 한다. 당신은 그와같은 일을 위해서 access 함수 ( 9. 8. 6절 [Access Permission] 참조. )를 사용할 수 있다; 그것은 유효 ID들보다는 실제 사용자와 그룹 ID들을 사용한다.


25. 11 누가 로그인 했는지 확인하기

이 절에 설명된 함수들을 사용해서 프로세스를 실행시키고 있는 사용자의 이름과 현재 세션에 로그인 된 사용자의 이름을 알아낼 수 있다. getuid와 friends 함수( 25. 5절 [Reading Persona] 참조. )를 또한 참조하라. getlogin 함수는 `unistd. h'에 선언되어 있고 cuserid 와 L_cuserid는 `stdio. h'에 선언되어 있다.

함수 : char *getlogin (void)

getlogin 함수는 프로세스가 제어하고 있는 터미널에 로그인 되어 있는 사용자의 이름이 포함된 문자열을 가리키는 포인터를 반환하거나, 만일 그 정보를 결정할 수 없다면 널 포인터를 반환한다. 문자열은 정적으로 할당되고 이 함수나 cuserid 함수의 연속적인 호출에 의해서 덧씌워진다.

함수 : char * cuserid (char *string)

cuserid 함수는 프로세스의 유효 ID와 연관된 사용자 이름이 포함된 string을 가리키는 포인터를 반환한다. 만일 string이 널 포인터가 아니라면, 그 string은 적어도 L_cuserid개의 문자를 저장할 수 있는 배열이 된다; 사용자의 이름이 포함된 문자열을 이 string에 반환된다. 그렇지 않다면, 정적인 지역에 있는 문자열을 가리키는 포인터가 반환된다. 이 string는 정적으로 할당되고 이 함수나 getlogin 함수의 연속적인 호출에 의해서 덧씌워질 것이다.

매크로 : int L__cuserid

사용자의 이름을 저장하기에 필요한 배열이 얼마나 길어야 하는지를 지적하는 정수 상수이다. 그 함수들은 이 세션에 로그인 된 사용자나 프로세스를 실행시키고 있는 사용자를 명확하게 확인하도록 허용한다. (그들은 setuid 프로그램이 포함되었을 때는 다를 수 있다; 25. 2절 [Process Persona] 참조. ) 사용자는 그 함수들을 속일 수 있는 방법이 아무 것도 없다.

대부분의 경우에, 사용자를 찾아내는 환경변수 LOGNAME를 사용하는 것이 더욱 유용하다. 이것은 사용자가 제멋대로 LOGNAME 을 설정할 수 있기 때문에 정밀함에 있어서는 좀더 유연성이 있다. 22. 2. 2절 [Standard Environment] 참조.


25. 12 사용자 데이터베이스

이 절은 등록된 사용자의 데이터베이스를 검색하고 찾기 위한 모든 것에 대해서 설명한다. 데이터베이스 자체는 거의 대부분의 시스템에서 `/etc/passwd'에 보존되어 있지만, 어떤 시스템에서는 특별한 네트웍 서버에게 그것을 억세스할 수 있도록 한다.

 

25. 12. 1 사용자의 정보를 담고 있는 데이터 구조체

시스템 사용자 데이터베이스를 억세스 하기 위하여 함수와 데이터 구조체들은 헤더파일 `pwd. h'에 선언되어 있다.

데이터타입 : struct passwd

passwd 데이터구조체는 시스템 사용자 데이터베이스 안에 엔트리들에 대한 정보를 저장하는데 사용된다. 그것은 적어도 다음과 같은 멤버들을 갖고 있다.

char *pw_name : 사용자 로그인 이름.

char *pw_passwd : 암호화된 비밀번호(password) 문자열.

uid_t pw_uid : 사용자 ID 번호.

gid_t pw_gid : 사용자의 디폴트 그룹 ID 번호.

char *pw_gecos

이 문자열은 전형적으로 사용자의 실제 이름을 포함하고, 가능하면 전화번호와 같은 다른 정보도 포함된다.

char *pw_dir

사용자의 홈 디렉토리(home directory), 또는 초기작업디렉토리. 이것에 대한 해석이 시스템-의존적인 경우에는 널 포인터가 되어야한다.

char *pw_shell

사용자의 디폴트 쉘, 이나 사용자가 로그인할 때 실행되는 초기 프로그램. 시스템에서 디폴트로 지정된 것이 사용되는 경우에는, 널 포인터가 되어야한다.

 

25. 12. 2 한 명의 사용자에 대해 자세히 알아보기

특정한 사용자에 대한 정보를 알아내기 위해서는, getpwuid 또는 getpwnam을 사용해서 시스템 사용자 데이터베이스를 검색할 수 있다. 그 함수들은 헤더파일 `pwd. h'에 선언되어 있다.

함수 : struct passwd * getpwuid (uid_t uid)

이 함수는 사용자 ID가 uid인 사용자에 대한 정보를 담고 있는 정적으로 할당된 구조체를 가리키는 포인터를 반환한다. 이 구조체는 getpwuid의 연속적인 호출에 의해서 덧씌워진다. 널 포인터 값은 데이터베이스에 사용자 ID가 uid인 사용자가 없다는 말이다.

함수 : struct passwd *getpwnam (const char *name)

이 함수는 사용자 이름이 name인 사용자에 대한 정보를 담고 있는 정적으로 할당된 구조체를 가리키는 포인터를 반환한다. 이 구조체는 getpwnam의 연속적인 호출로 덧씌워질 것이다. 널 포인터 값은 사용자의 이름이 name인 사용자가 데이터베이스에 없다는 말이다.

 

25. 12. 3 모든 사용자 리스트를 검색하기

이 절은 한 번에 한 사용자씩, 시스템에 있는 모든 사용자들의 리스트를 읽을 수 있기 위해서는 프로그램에서 어떻게 해야하는지를 설명한다. 이곳에 설명된 함수는 헤더파일 `pwd. h'에 선언되어 있다. 당신은 특정한 파일로부터 사용자 엔트리들을 읽기 위해서 fgetpwent 함수를 사용할 수 있다.

함수 : struct passwd * fgetpwent (FILE *stream)

이 함수는 stream 어로부터 다음 사용자 엔트리를 읽고 그 엔트리를 가리키는 포인터를 반환한다. 그 구조체는 정적으로 할당되었고, fgetpwent의 연속적인 호출로 덧씌워진다. 그러므로, 만일 당신이 그 정보를 저장하길 원한다면 구조체의 내용을 다른 곳에 복사해 놓아야만 한다. stream은 표준 password 데이터베이스 파일과 같은 형식의 파일이어야만 한다. 이 함수는 시스템 V 로부터 왔다.

사용자 데이터베이스의 모든 엔트리들을 검색하는 방법에는 setpwent, getpwent, 그리고 endpwent 가 있다.

함수 : void setpwent (void)

이 함수는 getpwent 가 사용자 데이터베이스를 읽기 위해서 사용하는 스트림을 초기화한다.

함수 : struct passwd * getpwent (void)

getpwent 함수는 setpwent 에 의해 초기화된 stream으로부터, 다음 엔트리를 읽고 엔트리를 가리키는 포인터를 반환한다. 그 구조체는 정적으로 할당되었고 getpwent의 연속적인 호출에 의해서 덧씌워진다. 그래서 만일 당신이 그 정보를 저장하기를 원한다면 구조체의 내용을 다른 곳으로 복사해놓아야만 한다.

함수 : void endpwent (void)

이 함수는 getpwent에 의해 사용된 내부 스트림을 닫는다.

 

25. 12. 4 사용자 엔트리를 기록하기

함수 : int putpwent (const struct passwd *p, FILE *stream)

이 함수는 표준 사용자 데이터베이스 파일에서 사용된 형식으로, 스트림 stream에 사용자 엔트리 *p를 기록한다. 성공하면 반환값은 0이고 실패하면 0이 아닌 값이다. SVID 와의 호환성을 위해서 이 함수는 존재한다. 우리는 구조체 struct passwd 가 위에 설명된 표준적인 것을 제외하고는 부가적으로 아무런 멤버들을 가지지 않았다고 예상이 되는 경우에만 이 함수를 사용하고, 그렇지 않은 경우들에는 이 함수를 사용하지 말 것은 권장한다; 전형적인 유닉스 데이터베이스와 더불어 사용자에 대한 다른 확장된 정보를 합병하는 시스템에서, 이 함수를 사용해서 엔트리를 더하는 것은 중요한 정보를 생략하게 되는 것이다.

함수 putpwent 는 `pwd. h'에 선언되어 있다.


25. 13 그룹 데이터 베이스

이 절은 등록된 그룹들의 데이터베이스를 어떻게 검색하고 찾을 것인지에 대한 모든 것이 설명되어 있다. 대부분의 시스템에서 데이터베이스 그 자체는 `/etc/group'에 보존되고 있는데, 어떤 시스템에서 특정한 네트웍 서비스는 그것에 대한 억세스를 제공한다.

 

25. 13. 1 그룹을 위한 데이터 구조체

시스템 그룹 데이터베이스를 억세스하기 위한 함수와 데이터구조체는 헤더파일 `grp. h'에 선언되어 있다.

데이터 타입 : struct group

group 구조체는 시스템 그룹 데이터베이스 안에 엔트리에 대한 정보를 저장하기 위해서 사용된다. 그 구조체는 적어도 다음과 같은 멤버들을 갖고 있다.

char *gr_name 그룹의 이름.

gid_t gr_gid 그룹의 그룹 ID

char **gr_mem

그룹에 있는 사용자들의 이름을 가리키는 포인터의 벡터. 각 사용자 이름은 널문자로 끝나는 문자열이고, 벡터 자체는 끝이 널 포인터로 끝난다.

 

25. 13. 2 한 개의 그룹에 대한 상세한 정보를 알아내기

getgrgid 나 getgrnam 을 사용해서 정해진 그룹에 대한 정보를 그룹 데이터베이스에서 찾아낼 수 있다. 그 함수들은 `grp. h'에 선언되어 있다.

함수 : struct group * getgrgid (gid_t gid)

이 함수는 그룹 ID 로 gid를 갖고 있는 그룹에 대한 정보를 갖고 있는 정적으로 할당된 구조체를 가리키는 포인터를 반환한다. 이 구조체는 getgrgid 의 연속적인 호출에 의해서 덧씌워질 것이다. 널 포인터는 그룹 ID로 gid 를 가진 그룹이 없다는 말이다.

함수 : struct group * getgrnam (const char *name)

이 함수는 그룹 이름으로 name을 가진 그룹에 대한 정보를 담고 있는 정적으로 할당된 구조체에 대한 포인터를 반환한다. 이 구조체는 getgrnam의 연속적인 호출에 의해서 덧씌워진다. 널 포인터는 그룹이름으로 name을 가진 그룹이 없다는 말이다.

 

25. 13. 3 모든 그룹에 대한 리스트를 검색하기

이 절은 프로그램에서 한 번에 한 그룹씩, 시스템에 있는 모든 그룹에 대한 리스트를 어떻게 읽을 수 있는지를 설명한다. 이곳에 설명된 함수들은 헤더파일 `grp. h'에 설명되어 있다. 특정한 파일로부터 그룹 엔트리들을 읽기 위해서 fgetgrent 함수를 사용할 수 있다.

함수 : struct group * fgetgrent (FILE *stream)

fgetgrent 함수는 stream으로부터 다음 엔트리를 읽고, 그 엔트리에 대한 포인터를 반환한다. 그 구조체는 정적으로 할당되었고 fgetgrent 의 연속적인 호출에 의해 덧씌워지기 때문에 만일 당신이 그 정보를 저장하기를 원한다면 그 구조체의 내용을 다른 곳으로 복사해놓아야만 한다. stream은 표준 그룹 데이터파일과 같은 형식을 가진 파일이어야만 한다.

그룹 데이터베이스에 있는 모든 엔트리들을 검색하는 방법은 setgrent, getgrent, 그리고 endgrent 가 있다.

함수 : void setgrent (void)

이 함수는 그룹 데이터베이스로부터 읽기 위한 스트림을 초기화한다. getgrent 함수의 호출에서 이 스트림이 사용된다.

함수 : struct group * getgrent (void)

getgrent 함수는 setgrent 에 의해 초기화된 스트림으로부터 다음 엔트리를 읽고 그 엔트리에 대한 포인터를 반환한다. 그 구조체는 정적으로 할당되었고 getgrent의 연속적인 호출에 의해서 덧씌워진다. 그래서 만일 당신이 그 정보를 저장하기를 원한다면 그 구조체의 내용을 다른 곳으로 복사해놓아야만 한다.

함수 : void entgrent (void)

이 함수는 getgrent 에 의해 사용된 내부스트림을 닫는다.


25. 14 사용자와 그룹 데이터베이스에 대한 예제

다음은 시스템 데이터베이스 질의 함수들을 사용하는 예를 보여주는 예제 프로그램이다. 다음 프로그램은 프로그램을 실행시키고 있는 사용자에 대한 어떤 정보를 출력한다.

#include <grp. h>
#include <pwd. h>
#include <sys/types. h>
#include <unistd. h>
#include <stdlib. h>
int
main (void)
{
uid_t me;
struct passwd *my_passwd;
struct group *my_group;
char **members;
/* 사용자 ID에 대한 정보를 얻어라. */
me = getuid ();
my_passwd = getpwuid (me);
if (!my_passwd) {
printf ("Couldn't find out about user %d. \n", (int) me);
exit (EXIT_FAILURE);
}
/* 정보를 출력하라. */
printf ("I am %s. \n", my_passwd->pw_gecos);
printf ("My login name is %s. \n", my_passwd->pw_name);
printf ("My uid is %d. \n", (int) (my_passwd->pw_uid));
printf ("My home directory is %s. \n", my_passwd->pw_dir);
printf ("My default shell is %s. \n", my_passwd->pw_shell);
/* 디폴트 그룹 ID에 대한 정보를 얻어라. */
my_group = getgrgid (my_passwd->pw_gid);
if (!my_group) {
printf ("Couldn't find out about group %d. \n",
(int) my_passwd->pw_gid);
exit (EXIT_FAILURE);
}
/* 정보를 출력하라. */
printf ("My default group is %s (%d). \n",
my_group->gr_name, (int) (my_passwd->pw_gid));
printf ("The members of this group are: \n");
members = my_group->gr_mem;
while (*members)
{
printf (" %s\n", *(members));
members++;
}
return EXIT_SUCCESS;
}
다음은 이 프로그램을 통한 출력이다.
I am Throckmorton Snurd.
My login name is snurd.
My uid is 31093.
My home directory is /home/fsg/snurd.
My default shell is /bin/sh.
My default group is guest (12).
The members of this group are:
friedman
tami