2.1.  PAM_ITEM데이터 얻고 설정하기

먼저 모듈에서 Linux-PAM 라이브러리 및 Linux-PAM 기반 응용에게 기대할 것들을 다룬다. 핵심은 libpam.* 라이브러리다.

2.1.1. 모듈 내부용 데이터 설정하기

#include <security/pam_modules.h>
int pam_set_data(pamh,  
 module_data_name,  
 data,  
 (*cleanup)(pam_handle_t *pamh, void *data, int error_status)); 
pam_handle_t *pamh;
const char *module_data_name;
void *data;
void (*cleanup)(pam_handle_t *pamh, void *data, int error_status);
 

2.1.1.1. 설명

pam_set_data 함수는 pamh 인자로 지정한 PAM 문맥 내에서 (바라건대) 유일한 문자열 module_data_name에 객체 포인터를 연계한다.

PAM 모듈이 동적 적재 오브젝트일 수 있다. 일반적으로 그런 파일들에는 static 변수가 있으면 안 된다. 이 함수와 pam_get_data(3) 함수는 모듈에서 핸들 pamh에 어떤 데이터를 연계시킬 수 있는 메커니즘을 제공한다. 보통 모듈에서 pam_set_data 함수를 호출해서 (바라건대) 유일한 module_data_name에다가 어떤 데이터를 등록한다. 그 데이터를 다른 모듈에서도 이용할 수 있다. 단, 응용에서는 이용할 수 없다. 이 함수는 data에 대한 포인터를 저장할 뿐이므로 모듈에서 그 내용물을 변경하거나 메모리를 해제해선 안 된다.

함수 cleanup()data에 연계되어서, 그 데이터를 덮어 쓸 때나 pam_end(3) 호출 다음에 NULL이 아니면 그 함수를 호출한다.

error_status 인자를 사용해 이 데이터 항목을 정리하기 위해 모듈에서 취해야 할 행동의 종류를 나타낸다. 예를 들어 커베로스에서 인증 과정 중에 티켓 파일을 만드는데, 그 파일을 데이터 항목에 연계해 둘 수 있을 것이다. pam_end(3)가 호출될 때 error_statuspam_authenticate(3) 또는 해당하는 다른 libpam 함수의 반환 값을 담는다. 그 값에 따라서 커베로스 모듈에서 (인증 실패인 경우) 티켓 파일을 삭제하거나, 그대로 둘 수 있다.

error_status가 다음 두 값 중 어느 쪽과도 논리 OR 되어 있을 수 있다.

PAM_DATA_REPLACE

데이터 항목을 (두 번째 pam_set_data 호출을 통해) 교체할 때 이 마스크를 쓴다. 이 마스크가 없으면 pam_end(3)로 인한 호출로 보면 된다.

PAM_DATA_SILENT

프로세스에서 cleanup()을 조용하게 수행하기를 원한다는 표시다. 즉 사용자에게 로그/메시지를 찍지 말아야 한다.

2.1.1.2. 반환 값

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_SUCCESS

데이터를 성공적으로 저장했다.

PAM_SYSTEM_ERR

PAM 핸들로 NULL 포인터를 줬거나, 함수를 응용에서 호출했다.

2.1.2. 모듈 내부용 데이터 얻기

#include <security/pam_modules.h>
int pam_get_data(pamh,  
 module_data_name,  
 data); 
const pam_handle_t *pamh;
const char *module_data_name;
const void **data;
 

2.1.2.1. 설명

이 함수와 pam_set_data(3) 함수를 함께 사용해서 호출자 PAM 모듈에서만 의미 있는 모듈별 데이터를 관리할 수 있다.

pam_get_data 함수는 pamh 인자로 지정한 PAM 문맥 내에서 (바라건대) 유일한 문자열 module_data_name에 연계된 객체를 찾는다. pam_get_data 호출이 성공하면 data가 객체를 가리키게 된다. 참고로 그 데이터는 사본이 아니므로 모듈에서 상수처럼 취급해야 한다.

2.1.2.2. 반환 값

PAM_SUCCESS

데이터를 성공적으로 가져왔다.

PAM_SYSTEM_ERR

PAM 핸들로 NULL 포인터를 줬거나, 함수를 응용에서 호출했다.

PAM_NO_MODULE_DATA

모듈 데이터를 찾을 수 없거나, 항목이 있지만 값이 NULL이다.

2.1.3. PAM 항목 설정하기

#include <security/pam_modules.h>
int pam_set_item(pamh,  
 item_type,  
 item); 
pam_handle_t *pamh;
int item_type;
const void *item;
 

2.1.3.1. 설명

pam_set_item 함수를 사용해 응용과 PAM 서비스 모듈에서 item_type이 나타내는 PAM 정보에 접근해 값을 갱신할 수 있다. 이를 위해 item 인자가 가리키는 객체의 사본을 만든다. 다음 item_type을 지원한다.

PAM_SERVICE

서비스 이름. (PAM 함수들에서 프로그램을 인증하는 데 이용할 PAM 스택을 나타낸다.)

PAM_USER

사용자 이름. 그 신원으로 서비스가 제공된다. 즉, 인증 후에 PAM_USER는 서비스를 이용하게 된 로컬 개체를 나타낸다. 참고로 PAM 스택의 모듈에서 이 값을 뭔가(예: "anonymous")에서 다른 뭔가(예: "guest119")로 매핑할 수 있다. 그러므로 응용에선 PAM 함수를 호출한 다음에 PAM_USER의 값을 확인해 봐야 한다.

PAM_USER_PROMPT

사용자 이름을 물을 때 보일 문자열. 이 문자열의 기본값은 "login: "의 지역화 버전이다.

PAM_TTY

터미널 이름. 장치 파일이면 앞에 /dev/가 붙는다. X 기반의 그래픽 응용에서 이 항목의 값은 $DISPLAY 변수여야 한다.

PAM_RUSER

요청하는 사용자의 이름. 로컬에서 요청하는 사용자면 로컬의 이름이고 원격에서 요청하는 사용자면 원격의 사용자 이름.

일반적으로 응용 내지 모듈에서는 강력하게 인증된 쪽을 (원격 계정보다는 로컬 계정을) 값으로 제공하려 하게 된다. 이 값의 신뢰성은 응용에 결부된 실제 인증 스택에 따라 정해지고, 그래서 궁극적으로 시스템 관리자에게 달려 있다.

PAM_RUSER@PAM_RHOST가 항상 요청하는 사용자를 나타낼 것이다. 일부 경우에는 PAM_RUSER가 NULL일 수 있다. 그 경우는 요청하는 개체가 누구인지 분명하지 않은 것이다.

PAM_RHOST

요청하는 호스트의 이름 (PAM_RUSER 개체가 서비스를 요청하고 있는 머신의 호스트명). 즉 PAM_RUSER@PAM_RHOST가 요청하는 사용자를 나타낸다. 일부 응용에서는 PAM_RHOST가 NULL일 수 있다. 그 경우는 인증 요청이 어디서 오는 것인지 분명하지 않은 것이다.

PAM_AUTHTOK

인증 토큰 (보통은 패스워드). pam_sm_authenticate(3)pam_sm_chauthtok(3)을 제외한 다른 모듈 함수에선 이 토큰을 무시해야 한다. 앞쪽 함수에선 한 모듈에서 다른 모듈로 최신 인증 토큰을 전달하는 데 쓴다. 뒤쪽 함수에선 다른 용도로 쓰는데, 현재 활성인 인증 토큰을 담는다.

PAM_OLDAUTHTOK

이전 인증 토큰. pam_sm_chauthtok(3)을 제외한 다른 모듈 함수에선 이 토큰을 무시해야 한다.

PAM_CONV

pam_conv 구조체. pam_conv(3) 참고.

다음 추가 항목들은 Linux-PAM 한정이므로 이식 가능한 응용에선 쓰지 말아야 한다.

PAM_FAIL_DELAY

실패 시 지연 공통 동작 방식을 바꾸기 위한 함수 포인터. pam_fail_delay(3) 참고.

PAM_XDISPLAY

X 디스플레이 이름. X 기반의 그래픽 응용에서 이 항목의 값은 $DISPLAY 변수여야 한다. PAM_TTY와 독립적으로 디스플레이 이름을 전달하는 데 이 값을 쓸 수 있다.

PAM_XAUTHDATA

필요시 PAM_XDISPLAY에 지정된 디스플레이로 연결하기 위해 필요한 X 인증 데이터를 담은 구조체에 대한 포인터. pam_xauth_data(3) 참고.

PAM_AUTHTOK_TYPE

기본적으로 모듈에서 패스워드를 요청할 때 "New UNIX password: " 및 "Retype UNIX passwords: " 프롬프트를 쓴다. 여기서 단어 UNIX를 이 항목으로 바꿀 수 있으며, 기본은 비어 있다. pam_get_authtok(3)에서 이 항목을 사용한다.

PAM_CONV와 PAM_FAIL_DELAY를 제외한 모든 item_type에서 item은 <NUL> 종료 문자열에 대한 포인터다. PAM_CONV에서 item은 초기화된 pam_conv 구조체를 가리킨다. PAM_FAIL_DELAY의 경우 itemvoid (*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr) 함수 포인터다.

PAM_AUTHOK과 PAM_OLDAUTHTOK 모두 응용으로 반환하기 전에 재설정된다. 즉 응용에서는 인증 토큰에 접근할 수 없다.

2.1.3.2. 반환 값

PAM_BAD_ITEM

정의돼 있지 않거나 접근 불가능한 항목을 설정하려 했음.

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_SUCCESS

데이터를 성공적으로 갱신했다.

PAM_SYSTEM_ERR

첫 번째 인자로 준 pam_handle_t가 유효하지 않다.

2.1.4. PAM 항목 얻기

#include <security/pam_modules.h>
int pam_get_item(pamh,  
 item_type,  
 item); 
const pam_handle_t *pamh;
int item_type;
const void **item;
 

2.1.4.1. 설명

pam_get_item 함수를 사용해 응용과 PAM 서비스 모듈에서 item_type이 나타내는 PAM 정보에 접근해 값을 가져올 수 있다. 성공 반환 시 item이 해당 항목의 값에 대한 포인터를 담고 있다. 참고로 실제 데이터에 대한 포인터이므로 free() 하거나 덮어 쓰면 안 된다. item_type으로 다음 값들을 지원한다.

PAM_SERVICE

서비스 이름. (PAM 함수들에서 프로그램을 인증하는 데 이용할 PAM 스택을 나타낸다.)

PAM_USER

사용자 이름. 그 신원으로 서비스가 제공된다. 즉, 인증 후에 PAM_USER는 서비스를 이용하게 된 로컬 개체를 나타낸다. 참고로 PAM 스택의 모듈에서 이 값을 뭔가(예: "anonymous")에서 다른 뭔가(예: "guest119")로 매핑할 수 있다. 그러므로 응용에선 PAM 함수를 호출한 다음에 PAM_USER의 값을 확인해 봐야 한다.

PAM_USER_PROMPT

사용자 이름을 물을 때 보일 문자열. 이 문자열의 기본값은 "login: "의 지역화 버전이다.

PAM_TTY

터미널 이름. 장치 파일이면 앞에 /dev/가 붙는다. X 기반의 그래픽 응용에서 이 항목의 값은 $DISPLAY 변수여야 한다.

PAM_RUSER

요청하는 사용자의 이름. 로컬에서 요청하는 사용자면 로컬의 이름이고 원격에서 요청하는 사용자면 원격의 사용자 이름.

일반적으로 응용 내지 모듈에서는 강력하게 인증된 쪽을 (원격 계정보다는 로컬 계정을) 값으로 제공하려 하게 된다. 이 값의 신뢰성은 응용에 결부된 실제 인증 스택에 따라 정해지고, 그래서 궁극적으로 시스템 관리자에게 달려 있다.

PAM_RUSER@PAM_RHOST가 항상 요청하는 사용자를 나타내게 된다. 일부 경우에는 PAM_RUSER가 NULL일 수 있다. 그 경우는 요청하는 개체가 누구인지 분명하지 않은 것이다.

PAM_RHOST

요청하는 호스트의 이름 (PAM_RUSER 개체가 서비스를 요청하고 있는 머신의 호스트명). 즉 PAM_RUSER@PAM_RHOST가 요청하는 사용자를 나타낸다. 일부 응용에서는 PAM_RHOST가 NULL일 수 있다. 그 경우는 인증 요청이 어디서 오는 것인지 분명하지 않은 것이다.

PAM_AUTHTOK

인증 토큰 (보통은 패스워드). pam_sm_authenticate(3)pam_sm_chauthtok(3)을 제외한 다른 모듈 함수에선 이 토큰을 무시해야 한다. 앞쪽 함수에선 한 모듈에서 다른 모듈로 최신 인증 토큰을 전달하는 데 쓴다. 뒤쪽 함수에선 다른 용도로 쓰는데, 현재 활성인 인증 토큰을 담는다.

PAM_OLDAUTHTOK

이전 인증 토큰. pam_sm_chauthtok(3)을 제외한 다른 모듈 함수에선 이 토큰을 무시해야 한다.

PAM_CONV

pam_conv 구조체. pam_conv(3) 참고.

다음 추가 항목들은 Linux-PAM 한정이므로 이식 가능한 응용에선 쓰지 말아야 한다.

PAM_FAIL_DELAY

실패 시 지연 공통 동작 방식을 바꾸기 위한 함수 포인터. pam_fail_delay(3) 참고.

PAM_XDISPLAY

X 디스플레이 이름. X 기반의 그래픽 응용에서 이 항목의 값은 $DISPLAY 변수여야 한다. PAM_TTY와 독립적으로 디스플레이 이름을 전달하는 데 이 값을 쓸 수 있다.

PAM_XAUTHDATA

필요시 PAM_XDISPLAY에 지정된 디스플레이로 연결하기 위해 필요한 X 인증 데이터를 담은 구조체에 대한 포인터. pam_xauth_data(3) 참고.

PAM_AUTHTOK_TYPE

기본적으로 모듈에서 패스워드를 요청할 때 "New UNIX password: " 및 "Retype UNIX passwords: " 프롬프트를 쓴다. 여기서 단어 UNIX를 이 항목으로 바꿀 수 있으며, 기본은 비어 있다. pam_get_authtok(3)에서 이 항목을 사용한다.

서비스 모듈에서 사용자의 이름을 얻고 싶으면 이 함수를 쓰지 말고 pam_get_user(3) 호출을 수행해야 한다.

서비스 모듈에서만 PAM_AUTHTOK 및 PAM_OLDAUTHTOK으로 인증 토큰을 읽을 권한이 있다.

2.1.4.2. 반환 값

PAM_BAD_ITEM

정의돼 있지 않거나 접근 가능하지 않은 항목을 설정하려 했다.

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_PERM_DENIED

item 값이 NULL이다.

PAM_SUCCESS

데이터를 성공적으로 가져왔다.

PAM_SYSTEM_ERR

첫 번째 인자로 준 pam_handle_t가 유효하지 않다.

2.1.5. 사용자 이름 얻기

#include <security/pam_modules.h>
int pam_get_user(pamh,  
 user,  
 prompt); 
const pam_handle_t *pamh;
const char **user;
const char *prompt;
 

2.1.5.1. 설명

pam_get_user 함수는 pam_start(3)로 지정한 사용자 이름을 반환한다. 사용자를 지정하지 않았으면 pam_get_item (pamh, PAM_USER, ... );의 반환 값을 반환한다. 그 값이 NULL이면 pam_conv(3) 메커니즘을 통해 사용자 이름을 얻는데, 다음 중 NULL 아닌 첫 문자열로 사용자에게 묻는다.

  • 함수에 준 prompt 인자.

  • pam_get_item (pamh, PAM_USER_PROMPT, ... );의 반환 값.

  • 기본 프롬프트 "login: "

어떤 방법으로든 얻은 사용자 이름에 대한 포인터가 *user 값으로 반환된다. 참고로 그 메모리를 모듈에서 free() 하거나 변경해선 안 된다.

이 함수에서는 pam_set_item(3)pam_get_item(3) 함수 관련 PAM_USER 항목을 설정한다.

2.1.5.2. 반환 값

PAM_SUCCESS

사용자 이름을 성공적으로 가져왔다.

PAM_SYSTEM_ERR

NULL 포인터를 줬다.

PAM_CONV_ERR

응용에서 제공한 대화 함수에서 사용자명을 얻는 데 실패했다.

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_ABORT

이전 대화를 재개하는 중 오류.

PAM_CONV_AGAIN

응용에서 제공한 대화 방법에서 이벤트를 기다리고 있다.

2.1.6. 대화 함수

#include <security/pam_appl.h>
struct pam_message {
    int msg_style;
    const char *msg;
};

struct pam_response {
    char *resp;
    int resp_retcode;
};

struct pam_conv {
    int (*conv)(int num_msg, const struct pam_message **msg,
                struct pam_response **resp, void *appdata_ptr);
    void *appdata_ptr;
};
  

2.1.6.1. 설명

PAM 라이브러리에선 응용에서 정의한 콜백을 이용해 응용과 적재된 모듈이 직접 소통할 수 있게 한다. 트랜잭션 시작 때 pam_start(3)에 주는 struct pam_conv로 콜백을 지정한다.

모듈에서 해당 conv() 함수를 호출할 때 그 구조체의 두 번째 항목을 appdata_ptr 인자에 설정한다.

conv() 호출의 나머지 인자들은 모듈과 응용 사이에 주고받는 정보에 대한 것이다. 말하자면 num_msg는 포인터의 배열인 msg의 길이를 담는다. 성공 반환 시 포인터 resp가 pam_response 구조체의 배열을 가리키는데, 그 구조체들은 응용에서 제공한 텍스트를 담는다. 그 구조체의 resp_retcode 멤버는 쓰지 않으며 0으로 설정해야 한다. 그 배열과 응답들을 free(3)로 해제하는 건 호출자의 책임이다. *resp가 포인터의 배열이 아니라 struct pam_response 배열이라는 점에 유의하자.

응답의 개수는 언제나 대화 함수 인자 num_msg와 같다. 따라서 대화 함수 호출 후마다 응답 배열이 free(3) 돼야 한다. 응답들의 인덱스는 pam_message 배열 프롬프트의 인덱스와 대응한다.

실패 시 대화 함수는 할당한 자원이 있으면 모두 해제한 후 미리 규정된 PAM 오류 코드들 중 하나를 반환해야 한다.

각 메시지는 네 가지 타입 중 하나이며, struct pam_messagemsg_style 멤버에 지정돼 있다.

PAM_PROMPT_ECHO_OFF

텍스트 반향 없이 문자열 얻기.

PAM_PROMPT_ECHO_ON

텍스트 반향하면서 문자열 얻기.

PAM_ERROR_MSG

오류 메시지 표시하기.

PAM_TEXT_INFO

어떤 텍스트 표시하기.

메시지가 배열로 돼 있으므로 모듈에서 한 번의 호출로 여러 가지를 응용으로 전달하는 게 가능해진다. 응용 입장에서도 관련 항목들이 한번에 오는 게 편할 수 있다. 그 경우 창 형태 응용에서 양식 하나로 여러 메시지/프롬프트를 한꺼번에 표시할 수 있다.

여담으로, Linux-PAM에서 대화 함수 인자 const struct pam_message **msg를 다루는 방식이 솔라리스의 PAM과 (또 그에 기반한 HP/UX 등과) 차이가 있다는 점을 언급해 둬야겠다. Linux-PAM은 msg 인자를 프로토타입 const struct pam_message *msg[]와 완전히 동등하게 해석한다. (다들 아는 main() 함수의 argv 인자에 흔히 쓰는 프로토타입이 char **argv와 char *argv[]인 것과 같은 식이다.) 요컨데 Linux-PAM에서는 msg 인자를 읽기 전용 'struct pam_message' 포인터 num_msg 개짜리 배열에 대한 포인터로 해석한다. 솔라리스 PAM 구현에서는 이 인자를 pam_message 구조체 num_msg개짜리 배열에 대한 포인터에 대한 포인터로 해석한다. 대부분 모듈 응용 개발자에게는 (아마도) 다행스럽게도 num_msg의 값이 1일 때 두 정의는 완전히 동등하다. 그러다 별 생각 없이 그 수를 2개로 올리면 예상치 않은 호환성 문제가 발생한다.

어느 쪽이 바람직한가는 제쳐 두고, 모듈 작성자가 두 가지 PAM 구현 모두와 소스 수준 호환성을 유지하기 위한 방법으로 두 가지가 있다.

  • 절대 num_msg를 1보다 크게 해서 대화 함수를 호출하지 않기.

  • msg를 이중 참조 구조로 만들어서 두 가지 대화 함수 모두 메시지를 찾아낼 수 있게 하기. 즉 다음처럼 만들기:

           msg[n] = & (( *msg )[n])
           

2.1.6.2. 반환 값

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_CONV_ERR

대화 실패. 응용에선 *resp를 설정하지 않아야 한다.

PAM_SUCCESS

성공.

2.1.7. PAM 환경 변수 설정 및 변경하기

#include <security/pam_appl.h>
int pam_putenv(pamh,  
 name_value); 
pam_handle_t *pamh;
const char *name_value;
 

2.1.7.1. 설명

pam_putenv 함수를 이용해 pamh 핸들에 연계된 PAM 환경 변수의 값을 추가하거나 바꾼다.

pamh 인자는 앞선 pam_start() 호출로 얻은 인증 핸들이다. name_value 인자는 단일 NUL 종료 문자열이며, 다음 중 한 형태다.

NAME=변수 값

이 경우 이름이 NAME인 환경 변수를 변수 값으로 설정한다. 이미 있는 변수면 덮어 쓴다. 아니면 PAM 환경에 추가한다.

NAME=

변수를 빈 값으로 설정한다. 그렇게 설정하려면 이 방식으로 해야 한다는 걸 보이기 위해 따로 나열한다.

NAME

'='가 없으면 pam_putenv() 함수에서 해당 변수를 PAM 환경에서 삭제하게 된다.

pam_putenv()는 name_value의 사본을 가지고 동작한다. 따라서 putenv(3)와 달리 응용에서 데이터의 메모리를 해제할 책임이 있다.

2.1.7.2. 반환 값

PAM_PERM_DENIED

name_value 인자를 NULL로 줬음.

PAM_BAD_ITEM

(삭제를) 요청한 변수가 현재 설정돼 있지 않음.

PAM_ABORT

pamh 핸들이 깨졌음.

PAM_BUF_ERR

메모리 버퍼 오류.

PAM_SUCCESS

환경 변수가 성공적으로 갱신되었음.

2.1.8. PAM 환경 변수 얻기

#include <security/pam_appl.h>
const char *pam_getenv(pamh,  
 name); 
pam_handle_t *pamh;
const char *name;
 

2.1.8.1. 설명

pam_getenv 함수는 pamh 핸들에 연계된 PAM 환경 목록에서 name이 가리키는 문자열과 일치하는 항목을 찾아서 그 환경 변수의 값에 대한 포인터를 반환한다. 응용에서 그 데이터의 메모리를 해제해선 안 된다.

2.1.8.2. 반환 값

pam_getenv 함수는 실패 시 NULL을 반환한다.

2.1.9. PAM 환경 얻기

#include <security/pam_appl.h>
char **pam_getenvlist(pamh); 
pam_handle_t *pamh;
 

2.1.9.1. 설명

pam_getenvlist 함수는 pamh 핸들에 연계된 PAM 환경의 전체 사본을 반환한다. 그 PAM 환경 변수들이 사용자가 인증돼서 서비스가 인가됐을 때의 일반 환경 변수들을 내용을 나타낸다.

메모리 형식은 char 포인터들의 malloc() 한 배열이다. 마지막 항목은 NULL로 설정돼 있다. 그 배열의 NULL 아닌 항목 각각이 "name=value" 형식으로 된 NUL 종료이고 malloc() 된 문자열을 가리킨다.

이 메모리를 libpam에서 절대 free() 하지 않는다는 점에 유의해야 한다. pam_getenvlist 호출로 얻은 후에는 호출한 응용에서 그 메모리를 free() 할 책임이 있다.

반환되는 배열의 형식과 내용이 execle(3) 함수 호출 세 번째 인자의 요건과 일치하는 건 우연이 아니라 의도된 것이다.

2.1.9.2. 반환 값

pam_getenvlist 함수는 실패 시 NULL을 반환한다.