리눅스 Tips, 리눅스 C/C++ 프로그래밍, 모바일 클라우드 동향 및 테스트 등

2013년 4월 9일 화요일

리눅스 시스템 프로그래밍 : 04 유저 환경(1)


1. 리눅스 계정 시스템

 리눅스는 다중 사용자 운영체제다. 이는 동시에 여러사람이 컴퓨터의 자원을 사용할 수 있음을 의미한다. 그러므로 리눅스 운영체제는 여러사람을 관리할 수 있는 시스템을 가지고 있어야 한다. 이것을 계정 시스템이라고 한다. 계정이란 컴퓨터 자원에 접근할 수 있는 사람들에 대한 정보로 이해하면 될 것이다.

 아니다. 엄밀히 따지자면, 사람이라는 표현은 잘못된것 같다. 운영체제의 계정에는 시스템 계정일반 사용자 계정 크게 두가지로 분류할 수 있는데, 사람이라하면 일반 사용자 계정만을 가리키는 것이기 때문이다. 그러므로 사람 대신 객체라고 표현하는게 옳은 것 같다. 다시 정리하자. 계정이란 컴퓨터 자원에 접근할 수 있는 객체에 대한 정보를 의미한다.


2. 계정 정보


 계정이라는 것은 컴퓨터 자원에 접근하기 위한 객체라는 것을 알게 되었다. 그렇다면, 이들 객체는 각 객체의 특징을 나타내주는 정보를 가지고 있을 것이다. 이것을 계정정보라고 하다. 계정정보는 다음과 같은 정보들을 포함하고 있을 것이다. 계정은 유저라고 부르기도 하며, 계정정보는 유저정보와 동일한 의미로 사용된다.

  • 계정이름 계정을 다른 계정과 분리시켜주는 이름
  • 권한 컴퓨터 자원은 그 한계가 있다. 무한대가 아니다. 또한 다중 사용자 운영체제인 리눅스에 의해서 접근하게 될경우, 시스템 보안데이터 보안의 차원에서 접근제어를 해야할 필요가 있다. 리눅스(:12)는 권한을 통해서 이를 관리한다. 즉 이 파일은 누구누구는 읽기만 가능하고, 어떤 그룹에 대해서는 읽기/쓰기가 모두 가능하다 라는 식으로 관리한다.
  • 패스워드 이름과 이름에 따른 권한이 주어졌다면, 이 권한을 요청한 유저가 정말로 합법적인 유저인지를 확인하는 과정을 거쳐야 할 것이다. 인증과정인 셈이다. 가장 널리 사용되는 방법은 아이디/패스워드를 통한 인증방법이다.

 위의 3가지 정보를 가지고 있으면, 완전한 하나의 계정을 만들어 낼 수 있다. 몇몇 부가적인 정보들이 더 필요한 경우가 있는데, 나머지는 말 그대로 부가정보들일 뿐이다.


3. 슈퍼유저


 컴퓨터 시스템은 회사와 매우 비슷한 면이 있다. 회사처럼 자원과 계정(사람)이 있으며, 권한이 부여된다. 입출입시 인증을 요구하기도 한다.

 회사에 CEO가 있다면, 운영체제(:12)에는 슈퍼유저(:12)가 있다. 슈퍼유저는 무엇이든지 할 수 있는 막강한 권한을 가진 특별한 유저를 칭한다. 회사의 경우 CEO라고 하더라도, 권한을 행사함에 있어서 여러가지 제약이 있는 반면, 슈퍼유저는 말그대로 슈퍼맨의 능력을 가지고 모든 능력을 행사할 수 있다. 다음과 같은 명령하나로 운영체제를 싹 날려버릴 수도 있다.

# rm -rf /

 전통적으로 유닉스에서 슈퍼유저는 root라는 계정이름이 부여된다. 리눅스 역시 유닉스의 이러한 전통을 따르고 있다. 슈퍼유저는 파일을 만들고 삭제하고, 파티션을 나누고, 유저를 추가하고 권한을 조정하는 등의 모든 업무를 처리할 수 있는 권한을 가지게 된다. 그 권한이 워낙 막강한 관계로, 최근의 몇몇 운영체제들은 root 라도 그 권한을 제한시키는 경우가 있다.


4. 유저 생성 과정


 아마도 여러분은 adduser(1) 명령을 이용해서 유저를 생성하는 법을 알고 있을 것이다. 여기에서는 실제 유저를 생성하기 위해서 어떤 과정을 거쳐야 하는지에 대해서 알아보도록 하겠다.




  4.1 유저 파일들


 리눅스에서 모든 정보는 파일을 통해서 관리된다는 것을 알고 있을 것이다. 유저정보 역시 마찬가지 이며, 유저와 관련된 파일들은 /etc 밑에 존재한다. 전통적으로 유닉스 시스템에서 /etc 디렉토리는 각종 설정파일을 저장하기 위한 목적으로 사용되어지고 있다. 유저 관련 주요 파일들은 다음과 같다.

passwd 유저 이름과 패스워드가 저장된다.
shadow passwd와 비슷하지만 보안이 강화되었다.
group 그룹 정보가 저장되어있다.
adduser.conf 유저생성과 관련된 변수들이 정의되어 있다.

각 파일들에 대한 내용은 아래에 다루도록 할 것이다.


  4.2 passwd 파일


 유저의 기본적인 정보는 /etc/passwd파일에 저장된다. 유저정보는 printable ASCII 문자로 입력이 되기 때문에 vi(:12)와 같은 에디터를 이용해서 내용을 확인할 수 있다. 다음은 passwd 파일 내용의 일부분이다.

# cat /etc/passwd
...
yundream:x:1000:1000:yundream,,,:/home/yundream:/bin/bash
mysql:x:110:121:MySQL Server,,,:/var/lib/mysql:/bin/false
mt-daapd:x:114:65534::/var/cache/mt-daapd:/bin/true
testyun:x:1004:1004:,,,:/home/testyun:/bin/bash
myyun:x:115:65534::/home/myyun:/bin/false
...
:를 구분자로 7개의 필드로 이루어진 간단한 구조를 하고 있다. 각 필드가 포함하는 내용은 다음과 같다.
유저:패스워드:UID:GID:GECOS:디렉토리:쉘
  1. 유저 : 시스템내에서 사용되는 유저 이름
  2. 패스워드 : 유저가 사용할 패스워드
  3. UID : 유저에게 부여되는 ID로 일련의 숫자다.
  4. GID : 유저가 포함되는 그룹의 ID로 일련의 숫자다.
  5. GECOS : 유저의 부가정보로 생략가능하다.
  6. 디렉토리 : 유저의 홈 디렉토리
  7. : 유저가 사용하게 될 shell(:12) 프로그램

 기본적으로 /etc/passwd 를 편집할 수 있다면, 어렵지 않게 유저를 추가시키는 프로그램을 생성할 수 있다. 마찬가지로 /etc/passwd 파일을 읽어들이는 것으로, 사용자의 정보를 얻어올 수도 있다. 파일의 권한설정등에 사용되는 값은 유저 이름이 아닌 UID이기 때문에, /etc/passwd 파일은 자주 분석될 필요가 있다. 이러한 분석 프로그램은 프로그래머가 직접 작성할 수도 있지만 몇몇 함수를 이용하면 쉽게 유저정보를 얻어올 수 있다. 이에 대해서는 마지막에 사용자 정보를 얻어오는 프로그램을 작성하는 것으로 알아보도록 할 것이다.

그런데, 이상한 점이 있다. 패스워드 영역이 x로 되어있다. 패스워드가 몽땅 x일리는 없을테고 !!?


  4.3 shadow password


 passwd 파일에 사용자 패스워드가 없다면, 어떻게 유저이름과 패스워드로 인증이 가능할 건지가 문제가 된다. 우선 passwd 파일의 문제점에 대해서 알아보도록 하자. 다음은 passwd 파일의 권한이다.

# ls -al /etc/passwd
-rw-r--r-- 1 root root 1522 2008-01-09 23:34 /etc/passwd

 누구든지 읽을 수 있게 되어있다. 단방향 암호화 되어 있기는 하지만, 암호화된 일련의 문자를 얻을 수 있다면, 패스워드 검사 프로그램을 만들어서 패스워드를 찾아낼 수 있다. 이는 보안상 심각한 문제라고 할 수 있다.

 그래서 shadow password라는 방식을 도입했다. /etc/passwd 에는 패스워드를 제외한 정보만을 저장하고, 실제 패스워드는 오직 root 계정만 읽을 수 있는 /etc/shadow파일에 저장하는 것으로 passwd 파일의 보안문제점을 해결한 방식이다. /etc/shadow 파일의 권한을 확인해 보면, 단지 root 계정만 읽을 수 있음을 알 수 있다. 일반 유저가 읽을 수 없기 때문에, 패스워드 보안을 달성하게 된다.

#ls -al /etc/shadow
-rw-r----- 1 root shadow 996 2008-01-09 23:34 /etc/shadow

 사실 사용자가 컴퓨터 시스템에 로그인 할것인지 안할 것인지를 결정하는 권한은 root 사용자만 가지고 있기 때문에, 다른 유저가 읽을 필요는 없을 것이다.

 단 일반 사용자도 패스워드를 제외한 다른 유저의 정보를 알아야할 필요가 있기 때문에, /etc/passwd 파일을 남겨두게 된다. 유저정보는 /etc/passwd 패스워드 정보는 /etc/shadow 를 통해서 유지된다. 다음은 shadow 파일에 저장된 정보들이다.

yundream:$1$1S6/q4Ed$7en1qZdeOVofyEjqaofy/:13663:0:99999:7:::

 역시 :를 구분자로 해서 필드를 구분하고 있다. 각 필드의 정보는 getspend(3) 메뉴얼 문서를 참고하기 바란다.

패스워드는 crypt(3)와 md5(:12)를 이용해서 단방향으로 암호화 된다.


5. 사용자 정보 얻어오기


 그럼 실제 사용자 정보를 얻어오는 방법에 대해서 알아보도록 하자. 우선 사용자 정보는 /etc/passwd 파일의 내용을 읽어와서 구분자 :로 필드를 쪼개고, 이것들을 구조체에 집어 넣는 방법을 생각해볼 수 있을 것이다.

 그러나 위의 방법은 너무 귀찮다. 다행히도 리눅스는 /etc/passwd 파일에서 사용자 정보를 얻어오기 위한 표준 라이브러리(:12) 함수인 getpwent함수를 제공하고 있다.

#include <pwd.h>
#include <sys/types.h>

struct passwd *getpwent(void);

 getpwent 함수는 /etc/passwd 에서 패스워드 정보를 읽어들여서 passwd 구조체에 저장하고, 이것에 대한 포인터를 되돌려준다. passwd 구조체는 다음과 같이 정의되어 있다.


struct passwd
{
    char  *pw_name;     /* 유저 이름 */
    char  *pw_passwd;   /* 유저 패스워드 */
    uid_t pw_uid;       /* 유저 ID (UID) */ 
    gid_t pw_gid;       /* 그룹 ID (GID) */ 
    char  *pw_gecos;    /* 실제 이름 */
    char  *pw_dir;      /* 홈 디렉토리 */
    char  *pw_shell;    /* 사용자 쉘 */
};

다음은 getpwent()함수를 이용해서 유저정보를 읽어오는 간단한 프로그램이다.

#include <pwd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

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

    struct passwd *pass_info = NULL;

    while((pass_info = getpwent()) != NULL)
    {
        if (strncmp(name, pass_info->pw_name, strlen(name)) == 0)
        {
            printf("%12s uid(%d) gid(%d) home(%s)\n",
                pass_info->pw_name,
                pass_info->pw_uid,
                pass_info->pw_gid,
                pass_info->pw_dir);
            return 0;
        }
    }
    printf ("Can not find User : %s\n", name);
    return 1;
}

댓글 1개:

  1. 출처
    http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/Book_LSP/ch04_User

    답글삭제