Index: Makefile.in =================================================================== --- Makefile.in (revisión: 626) +++ Makefile.in (copia de trabajo) @@ -27,7 +27,7 @@ prefix = @prefix@ -SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ +SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ @pam@ ERLSHLIBS = expat_erl.so SOURCES = $(wildcard *.erl) BEAMS = $(SOURCES:.erl=.beam) Index: configure.ac =================================================================== --- configure.ac (revisión: 626) +++ configure.ac (copia de trabajo) @@ -16,6 +16,8 @@ AM_WITH_EXPAT #locating zlib AM_WITH_ZLIB +#locating zlib +AM_WITH_ZLIB # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -27,6 +29,7 @@ AC_FUNC_MALLOC AC_HEADER_STDC +AC_MOD_ENABLE(pam, yes) AC_MOD_ENABLE(mod_pubsub, yes) AC_MOD_ENABLE(mod_irc, yes) AC_MOD_ENABLE(mod_muc, yes) @@ -55,6 +58,7 @@ AC_SUBST(db_type) AC_CONFIG_FILES([Makefile + $make_pam $make_mod_irc $make_mod_muc $make_mod_pubsub Index: pam/stress_pam.erl =================================================================== --- pam/stress_pam.erl (revisión: 0) +++ pam/stress_pam.erl (revisión: 0) @@ -0,0 +1,11 @@ +-module(stress_pam). +-export([start/0]). + +start() -> + Port = pam:start(), + stress(Port). + +stress(Port) -> + true = pam:check_password(Port, "user", "password"), + timer:sleep(200), + stress(Port). Index: pam/Makefile.in =================================================================== --- pam/Makefile.in (revisión: 0) +++ pam/Makefile.in (revisión: 0) @@ -0,0 +1,35 @@ +# $Id: Makefile.in 285 2004-11-05 21:14:31Z aleksey $ + +CC = @CC@ +CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@ + +SUBDIRS = + +ERLSHLIBS = ../pam_drv.so + +OUTDIR = .. +EFLAGS = -I .. -pz .. +OBJS = \ + $(OUTDIR)/pam.beam + +all: $(OBJS) $(ERLSHLIBS) + +$(OUTDIR)/%.beam: %.erl + @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< + +$(ERLSHLIBS): ../%.so: %.c + $(CC) -Wall $(CFLAGS) $(LDFLAGS) \ + $(subst ../,,$(subst .so,.c,$@)) $(LIBS) \ + -o $@ -fpic -shared + +clean: + rm -f $(OBJS) $(ERLSHLIBS) + +distclean: clean + rm -f Makefile + +TAGS: + etags *.erl Index: pam/pam_drv.c =================================================================== --- pam/pam_drv.c (revisión: 0) +++ pam/pam_drv.c (revisión: 0) @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include + +typedef struct _PromptMatch +{ + struct _PromptMatch* next; + char* prompt; + char* response; +} *PromptMatch; + +static PromptMatch _newMatch(PromptMatch list, char* prompt, char* response) +{ + PromptMatch result = (PromptMatch)malloc(sizeof(struct _PromptMatch)); + memset(result, '\0', sizeof(struct _PromptMatch)); + result->prompt = prompt; + result->response = response; + if (list != NULL) + { + list->next = result; + return list; + } + else + { + return result; + } +} + +static char* _findMatch(PromptMatch list, const char* prompt) +{ + PromptMatch cur = list; + while (cur != NULL) + { + if (strstr(prompt, cur->prompt) != 0) + { + return cur->response; + } + cur = cur->next; + } + return NULL; +} + +static void _cleanupMatch(PromptMatch list) +{ + PromptMatch cur = list; + PromptMatch next; + while (cur != NULL) + { + free(cur->prompt); + free(cur->response); + next = cur->next; + free(cur); + cur = next; + } +} + +static int _handle_pam_conversation(int msgcnt, const struct pam_message** msgs, struct pam_response** res, + void* arg) +{ + int i; + PromptMatch prompts = (PromptMatch)arg; + struct pam_response *reply; + + if(msgcnt <= 0) + { + return PAM_CONV_ERR; + } + + // Allocate a response for each message + reply = (struct pam_response *) malloc(sizeof(struct pam_response) * msgcnt); + memset(reply, 0, sizeof(struct pam_response) * msgcnt); + + // Walk each message from PAM and lookup the response corresponding to each prompt + for(i = 0; i < msgcnt; i++) + { + if(msgs[i]->msg_style == PAM_PROMPT_ECHO_OFF || msgs[i]->msg_style == PAM_PROMPT_ECHO_ON) + { + // Find the response for the current prompt; if none is found, warn + char* response = _findMatch(prompts, msgs[i]->msg); + if (response != NULL) + { + // Copy password into response + reply[i].resp = strdup(response); + reply[i].resp_retcode = 0; + } + else + { + printf("Warning: No prompt found for: %s\n", msgs[i]->msg); + reply[i].resp = strdup("Unexpected prompt"); + reply[i].resp_retcode = 0; + } + } + } + + *res = reply; + + return PAM_SUCCESS; +} + +#define DECODE_STRING(buffer, index, result) \ + { \ + int tmp = 0; int size = 0; \ + ei_get_type(buffer, index, &tmp, &size); \ + result = malloc(size + 1); \ + ei_decode_string(buffer, index, result); \ + } + +#define RETURN_INT(value) \ + { \ + ErlDrvBinary* b = driver_alloc_binary(1); \ + rlen = 1; \ + b->orig_bytes[0] = value; \ + *rbuf = (char*)b; \ + return rlen; \ + } + +static int pam_erl_start(ErlDrvPort port, char* command) +{ + set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); + return 0; +} + +static int pam_erl_control(ErlDrvData drv_data, unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + int tmp = 0; + int index = 0; + int size = 0; + char* username = 0; + char* password = 0; + + int i = 0; + int rc = 0; + struct pam_conv conv; + pam_handle_t* pam; + + // Parse the version and tuple header + ei_decode_version(buf, &index, &tmp); + ei_decode_tuple_header(buf, &index, &tmp); + if (tmp != 2) + { + printf("Incorrect number of arguments!\n"); + RETURN_INT(-1); + } + + // The first item in the tuple should be the username + DECODE_STRING(buf, &index, username); + + // The second item in the tuple should be a list of tuples + // Each tuple in the list consists of the expected prompt string + // and the value to return at that prompt + ei_decode_list_header(buf, &index, &size); + + // Loop over the list and pull out each prompt/response tuple and + // store it into a PromptItem list that will get processed in + // the PAM conversation + PromptMatch head = NULL; + for (i = 0; i < size; i++) + { + int tuplecnt = 0; + char* prompt = 0; + char* response = 0; + + ei_decode_tuple_header(buf, &index, &tuplecnt); + if (tuplecnt != 2) + { + printf("Incorrect number of arguments in prompt/response tuple: %d\n", tuplecnt); + _cleanupMatch(head); + free(username); + RETURN_INT(-2); + } + + // Decode the prompt from the tuple + DECODE_STRING(buf, &index, prompt); + DECODE_STRING(buf, &index, response); + + // Add a new prompt item to track this prompt/response pair + head = _newMatch(head, prompt, response); + } + + // Setup PAM conversation structure; pass in the prompt match list + // so we can process the prompts from PAM + conv.conv = _handle_pam_conversation; + conv.appdata_ptr = head; + + // Spin up PAM + rc = pam_start("login", username, &conv, &pam); + if (rc != PAM_SUCCESS) + { + printf("Unable to spin up PAM: %s\n", pam_strerror(NULL, rc)); + RETURN_INT(-3); + } + + // Attempt to authenticate the user + rc = pam_authenticate(pam, 0); + if (rc != PAM_SUCCESS) + { + pam_end(pam, rc); + printf("Authentication failed for: %s. Error: %s\n", username, pam_strerror(NULL, rc)); + free(password); + free(username); + RETURN_INT(1); + } + + pam_end(pam, rc); + + _cleanupMatch(head); + free(username); + + RETURN_INT(0); +} + +ErlDrvEntry pam_driver_entry = { + NULL, /* F_PTR init, N/A */ + pam_erl_start, /* L_PTR start, called when port is opened */ + NULL, /* F_PTR stop, called when port is closed */ + NULL, /* F_PTR output, called when erlang has sent */ + NULL, /* F_PTR ready_input, called when input descriptor ready */ + NULL, /* F_PTR ready_output, called when + output descriptor ready */ + "pam_drv", /* char *driver_name, the argument to open_port */ + NULL, /* F_PTR finish, called when unloaded */ + NULL, /* handle */ + pam_erl_control, /* F_PTR control, port_command callback */ + NULL, /* F_PTR timeout, reserved */ + NULL /* F_PTR outputv, reserved */ +}; + +DRIVER_INIT(pam_drv) /* must match name in driver_entry */ +{ + return &pam_driver_entry; +} Index: pam/pam_test.c =================================================================== --- pam/pam_test.c (revisión: 0) +++ pam/pam_test.c (revisión: 0) @@ -0,0 +1,68 @@ +#include +#include +#include + +static int _handle_pam_conversation(int msgcnt, const struct pam_message** msgs, struct pam_response** res, + void* arg) +{ + int i; + struct pam_response *reply; + + if(msgcnt <= 0) + { + return PAM_CONV_ERR; + } + + // Allocate a response for each message + reply = (struct pam_response *) malloc(sizeof(struct pam_response) * msgcnt); + memset(reply, 0, sizeof(struct pam_response) * msgcnt); + + // Walk each message from PAM and lookup the response corresponding to each prompt + for(i = 0; i < msgcnt; i++) + { + printf("PAM Message(%d): %s\n", msgs[i]->msg_style, msgs[i]->msg); + reply[i].resp = strdup("Unexpected prompt"); + reply[i].resp_retcode = 0; + } + + *res = reply; + + return PAM_SUCCESS; +} + + +int main(int args, char** argv) +{ + struct pam_conv conv; + pam_handle_t* pam; + int rc; + + if (args < 3) + { + printf("Please provide a username and service as the first and second arguments!\n"); + return -1; + } + + char* username = argv[1]; + char* service = argv[2]; + + // Setup PAM conversation structure; pass in the prompt match list + // so we can process the prompts from PAM + conv.conv = _handle_pam_conversation; + conv.appdata_ptr = 0; + + // Spin up PAM + rc = pam_start(service, username, &conv, &pam); + if (rc != PAM_SUCCESS) + { + printf("Unable to spin up PAM: %s\n", pam_strerror(NULL, rc)); + return -3; + } + + // Attempt to authenticate the user + rc = pam_authenticate(pam, 0); + + pam_end(pam, rc); + + return 0; +} Index: pam/pam.erl =================================================================== --- pam/pam.erl (revisión: 0) +++ pam/pam.erl (revisión: 0) @@ -0,0 +1,19 @@ +-module(pam). +-author('dizzyd@dizzyd.com'). + +-export([start/0, + check_password/3]). + +start() -> + ok = erl_ddll:load_driver(ejabberd:get_so_path(), pam_drv), + open_port({spawn, pam_drv}, [binary]). + +check_password(Port, Username, Password) -> + Bin = term_to_binary({Username, [{"Password:", Password}]}), + Res = port_control(Port, 0, Bin), + case Res of + <<0>> -> + true; + _ -> + false + end. Index: aclocal.m4 =================================================================== --- aclocal.m4 (revisión: 626) +++ aclocal.m4 (copia de trabajo) @@ -292,3 +292,32 @@ fi ]) dnl + +AC_DEFUN(AM_WITH_PAM, +[ AC_ARG_WITH(pam, [ --with-pam=PREFIX prefix where PAM is installed ]) +unset PAM_LIBS; +unset PAM_CFLAGS; + +if test x"$with_pam" != x; then + PAM_CFLAGS="-I$with_pam/include" + PAM_LIBS="-L$with_pam/lib" +fi + +AC_CHECK_LIB(pam, pam_start, + [ PAM_LIBS="$PAM_LIBS -lpam" pam_found=yes ], + [ pam_found=no ], + "$PAM_LIBS") + +if test $pam_found = yes; then + pam_save_CFLAGS="$CFLAGS" + CFLAGS="$PAM_CFLAGS $CFLAGS" + AC_CHECK_HEADERS(security/pam_appl.h, ,$pam_found=no) + if test $pam_found = no; then + AC_MSG_ERROR([Could not find security/pam_appl.h]) + fi + CFLAGS=$pam_save_CFLAGS + + AC_SUBST(PAM_CFLAGS) + AC_SUBST(PAM_LIBS) +fi +])