[리눅스 시스템 프로그래밍] 8.2 사용자 ID와 그룹 ID 설정, setuid, setgid, setreuid, setregid, seteuid, setegid

리눅스 시스템에서는 사용자 ID와 그룹 ID를 변경하는 시스템 호출을 제공하고 있어요.

#include <sys/types.h >

int setuid(uid_t uid);

int setgid(gid_t gid);

int setreuid(uid_t ruid, uid_t euid);

int setregid(gid_t rgid, gid_t egid);

int seteuid(uid_t uid);

int setegid(gid_t gid);

int setuid(uid_t uid);

int setgid(gid_t gid);

setuid 함수를 이용하여 실제 사용자 ID(Real User ID)와 유효 사용자 ID(Effective User ID)를 변경합니다. 그리고 setgid 함수는 실제 그룹 ID와 유효 그룹 ID를 변경합니다. 그런데 이는 프로세스의 유효 사용자 ID 권한에 따라 달라집니다.

만약 슈퍼유저 권한을 갖는다면 실제 사용자 ID, 유효 사용자 ID와 saved set user id를 입력인자로 전달한 값으로 설정합니다.

만약 슈퍼유저 권한은 없지만 실제 사용자 ID 혹은 saved set user id와 같다면 유효 사용자 ID를 입력 인자로 전달한 값으로 변경합니다. 하지만 실제 사용자 ID와 saved set user id는 변하지 않습니다.

다음은 프로그램 시작 시에 실제 사용자 ID와 유효 사용자 ID를 출력하고 명령행 인자로 전달한 uid를 입력 인자로 setuid 함수를 호출한 후에 다시 실제 사용자 ID와 유효 사용자 ID를 출력하는 예제 코드입니다.

/**********************************************************************
* ex_setuid.c                                                         *
* exmple source – set user id                                         *
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        printf("Usage: %s [user id]\n",argv[0]);
        printf("*** user id is number ***\n");
        return 1;
    }

    printf("real user id:%d effective user id:%d\n",getuid(), geteuid());
    uid_t uid=atoi(argv[1]);
    setuid(uid);
    printf("real user id:%d effective user id:%d\n",getuid(), geteuid());

    return 0;
}

다음은 ehpub 계정 소유의 ex_setuid 실행 파일에 set user id bit를 설정한 후에 ehclub으로 계정을 전환하여 실행하였을 때 유효 사용자 ID가 원하는 값으로 설정한다는 것을 실험한 화면입니다.

실험에서 ehclub 계정으로 실행하여 자신의 user id(1001)로 setuid를 호출하여 설정 요청을 했을 때 유효 사용자 id를 자신의 user id로 변경하는 것을 알 수 있습니다. 만약 setuid함수에 유효 사용자 ID(1000)를 전달하면 원래 값과 차이가 없어서 아무런 변화가 없음을 알 수 있습니다. 그리고 setuid 함수에 0(슈퍼유저 ID)를 전달하면 권한이 부족하여 오류가 발생함을 알 수 있습니다. 만약 이것이 가능하면 유효 사용자 ID를 손쉽게 슈퍼유저 ID로 변경하여 시스템을 엉망으로 만들 수 있기에 이를 허용하지 않는 것입니다. 주의할 점은 set user id 비트를 설정하면 프로세스의 실행 권한은 파일의 소유자 권한이라는 점입니다.

실험에서는 다시 한 번 set user id bit를 제거한 후에 다시 실행하였을 때는 set user id bit가 해제 상태여서 똑같은 실험을 다시 한 번 하고 있습니다. ehclub 계정의 사용자 ID(1001)를 입력 인자로 전달하면 이미 실제 사용자 ID와 유효 사용자 ID 모두 1001이므로 아무런 변화를 갖지 않습니다. 그리고 그 외의 값을 전달하면 권한이 부족하여 설정하지 못함을 알 수 있어요.

[그림 8.5] ex_setuid 실행 화면
[그림 8.5] ex_setuid 실행 화면

그리고 다음 실행 화면은 슈퍼유저일 때에는 setuid 함수에 전달한 인자 값으로 실제 사용자 ID와 유효 사용자 ID를 설정하는 것을 볼 수 있습니다.

[그림 8.6] ex_setuid 실행 화면2
[그림 8.6] ex_setuid 실행 화면2

int setreuid(uid_t ruid, uid_t euid);

int setregid(gid_t rgid, gid_t egid);

리눅스 시스템에서는 setreuid 함수를 제공하여 실제 사용자 ID와 유효 사용자 ID를 설정할 수 있습니다. 이 때도 실행 권한을 갖고 있어야 합니다.

다음은 setreuid 함수를 이용하여 명령행에서 전달한 두 개의 user id로 실제 사용자 ID와 유효 사용자 ID를 설정하는 예제 코드입니다.

/**********************************************************************
* ex_setreuid.c                                                       *
* exmple source – set real user id and effective user id              *
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        printf("Usage: %s [real user id] [effective user id]\n",argv[0]);
        printf("*** user id is number ***\n");
        return 1;
    }

    printf("real user id:%d effective user id:%d\n",getuid(), geteuid());
    uid_t ruid=atoi(argv[1]);
    uid_t euid = atoi(argv[2]);
    if(setreuid(ruid,euid)==0)
    {
        printf("real user id:%d effective user id:%d\n",getuid(), geteuid());
    }
    else
    {
        perror("error setreuid");
    }

    return 0;
}

다음은 ex_setreuid 실행 파일에 set user id 비트를 설정한 후에 다른 사용자 계정으로 전환한 후에 현재 실제 사용자 ID와 유효 사용자 ID를 변경하는 것을 실행하고 있습니다. 주의할 점은 set user id 비트를 설정하면 프로세스의 실행 권한은 파일의 소유자 권한이라는 점입니다.

[그림 8.7] ex_setreuid 실행 화면
[그림 8.7] ex_setreuid 실행 화면

다음은 ex_setreuid 실행 파일에 set user id 비트를 해제한 후에 일반 사용자 계정으로 실행하면 setreuid 함수를 수행할 권한이 없다는 오류가 발생하고 슈퍼유저 계정으로 실행하면 원하는 값으로 실제 사용자 ID와 유효 사용자 ID를 변경할 수 있음을 실험한 화면입니다.

[그림 8.8] ex_setreuid 실행 화면2
[그림 8.8] ex_setreuid 실행 화면2

int seteuid(uid_t uid);

int setegid(gid_t gid);

리눅스 시스템에서는 유효 사용자 ID를 설정하는 seteuid를 제공하고 있어요. 다음은 명령행 인자로 전달한 값으로 유효 사용자 ID를 설정하는 예제 코드입니다.

/**********************************************************************
* ex_setuid.c                                                         *
* exmple source – set user id                                         *
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        printf("Usage: %s [user id]\n",argv[0]);
        printf("*** user id is number ***\n");
        return 1;
    }

    printf("real user id:%d effective user id:%d\n",getuid(), geteuid());
    uid_t uid=atoi(argv[1]);
    if(setuid(uid)==0)
    {
        printf("real user id:%d effective user id:%d\n",getuid(), geteuid());
    }
    else
    {
        perror("error setuid");
    }

    return 0;
}

다음은 ex_seteuid 실행 파일에 set user id 비트를 설정한 후에 일반 계정으로 실행하였을 때와 슈퍼유저 계정으로 실행하였을 때의 모습이다. 주의할 점은 set user id 비트를 설정하면 프로세스의 실행 권한은 파일의 소유자 권한이라는 점입니다.

[그림 8.9] ex_seteuid 실행 화면
[그림 8.9] ex_seteuid 실행 화면

그리고 다음 화면은 ex_seteuid 실행 파일에 set user bit를 해제한 후에 실행한 화면입니다. 일반 계정은 권한이 부족하여 자신이 아닌 다른 사용자 ID로 설정하려고 할 때 오류를 발생하는 것을 알 수 있어요. 반면 슈퍼유저 권한에서는 정상적으로 유효 사용자 ID를 설정하는 것을 알 수 있어요.

[그림 8.10] ex_seteuid 실행 화면2
[그림 8.10] ex_seteuid 실행 화면2