#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; };
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_message의 msg_style 멤버에 지정돼 있다.
텍스트 반향 없이 문자열 얻기.
텍스트 반향하면서 문자열 얻기.
오류 메시지 표시하기.
어떤 텍스트 표시하기.
메시지가 배열로 돼 있으므로 모듈에서 한 번의 호출로 여러 가지를 응용으로 전달하는 게 가능해진다. 응용 입장에서도 관련 항목들이 한번에 오는 게 편할 수 있다. 그 경우 창 형태 응용에서 양식 하나로 여러 메시지/프롬프트를 한꺼번에 표시할 수 있다.
여담으로, 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])