Index: Makefile.in
===================================================================
--- Makefile.in	(revisión: 705)
+++ 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: ejabberd.cfg.example
===================================================================
--- ejabberd.cfg.example	(revisión: 705)
+++ ejabberd.cfg.example	(copia de trabajo)
@@ -95,7 +95,14 @@
 %{auth_method, odbc}.
 %{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
 
+% If you want to allow both internal and PAM authentication:
+{auth_method, [internal, pam]}.
+% Default PAM service "login" can be changed with:
+%{pam_service, "login"}.
+% Default PAM prompt "Password:" can be changed with:
+%{pam_prompt_pwd, "Password:"}.
 
+
 % Host name:
 {hosts, ["localhost"]}.
 
Index: configure.ac
===================================================================
--- configure.ac	(revisión: 705)
+++ configure.ac	(copia de trabajo)
@@ -16,6 +16,10 @@
 AM_WITH_EXPAT
 #locating zlib
 AM_WITH_ZLIB
+#locating zlib
+AM_WITH_ZLIB
+#locating Linux-PAM
+AM_WITH_PAM
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -27,6 +31,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 +60,7 @@
 AC_SUBST(db_type)
 
 AC_CONFIG_FILES([Makefile
+                 $make_pam
                  $make_mod_irc
                  $make_mod_muc
                  $make_mod_pubsub
Index: pam/Makefile.in
===================================================================
--- pam/Makefile.in	(revisión: 0)
+++ pam/Makefile.in	(revisión: 0)
@@ -0,0 +1,35 @@
+# $Id: Makefile.in 285 2006-12-11 10:16:21Z $
+
+CC = @CC@
+CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@
+
+SUBDIRS = 
+
+ERLSHLIBS = ../ejabberd_auth_pam.so
+
+OUTDIR = ..
+EFLAGS = -I .. -pz ..
+OBJS   = \
+	$(OUTDIR)/ejabberd_auth_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/ejabberd_auth_pam.c
===================================================================
--- pam/ejabberd_auth_pam.c	(revisión: 0)
+++ pam/ejabberd_auth_pam.c	(revisión: 0)
@@ -0,0 +1,255 @@
+#include <stdio.h>
+#include <string.h>
+#include <ei.h>
+#include <erl_driver.h>
+#include <security/pam_appl.h>
+
+#ifdef LINUX_PAM        /* XXX: and OpenPAM? */
+# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
+#else
+# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
+#endif
+
+//
+#ifndef LINKEDLIBPAM
+#include <dlfcn.h>
+
+#ifndef LIBPAMPATH
+#define LIBPAMPATH "/usr/lib/libpam.so"
+#endif
+
+static void *libpam_h = NULL;
+
+void init_libpam() {
+  if ( libpam_h == NULL ) {
+    libpam_h = dlopen( LIBPAMPATH, RTLD_GLOBAL | RTLD_NOW );
+    printf( "Open: %s\r\n", LIBPAMPATH );
+  };
+};
+
+void fin_libpam() {
+  if ( libpam_h != NULL ) {
+    dlclose( libpam_h );
+    libpam_h = NULL;
+    printf( "Close: %s\r\n", LIBPAMPATH );
+  };
+};
+#endif
+//
+
+typedef struct _PromptMatch {
+  struct _PromptMatch* next;
+  char* prompt;
+  char* response;
+} *PromptMatch;
+
+typedef struct {
+  ErlDrvPort port;
+} ejabberd_pam_data;
+
+static PromptMatch _newMatch( PromptMatch list, char* prompt, char* response ) {
+  PromptMatch result = ( PromptMatch)malloc( sizeof( struct _PromptMatch ));
+  bzero( result, sizeof( struct _PromptMatch ));
+  result->prompt = prompt;
+  result->response = response;
+  result->next = list;
+  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 ) {
+    next = cur->next;
+    free( cur->prompt );
+    free( cur->response );
+    free( cur );
+    cur = next;
+  };
+};
+
+static int pam_conversation( int msgcnt, const struct pam_message** msgs, struct pam_response** res, void* arg ) {
+  int iResult = PAM_CONV_ERR;
+  if (( msgcnt > 0 ) || ( msgcnt <= PAM_MAX_NUM_MSG )) {
+    // Allocate a response for each message
+    int iSizeResponse = sizeof( struct pam_response ) * msgcnt;
+    struct pam_response *reply = (struct pam_response *)malloc( iSizeResponse );
+    if ( reply != NULL ) {    
+      bzero( reply, iSizeResponse );      
+      // Walk each message from PAM and lookup the response corresponding to each prompt
+      int ii;
+      char* response;
+      for ( ii = 0; ii < msgcnt; ii++ ) {
+        printf( "prompt(index %d)(style %d): %s\r\n", ii, PAM_MSG_MEMBER( msgs, ii, msg_style ), PAM_MSG_MEMBER( msgs, ii, msg ));
+        switch ( PAM_MSG_MEMBER( msgs, ii, msg_style )) {
+          case PAM_PROMPT_ECHO_ON :
+          case PAM_PROMPT_ECHO_OFF :
+            // Find the response for the current prompt; if none is found, warn
+            response = _findMatch( (PromptMatch)arg, PAM_MSG_MEMBER( msgs, ii, msg ));
+            if ( response != NULL ) {
+              // Copy password into response
+              reply[ ii ].resp = strdup( response );
+              reply[ ii ].resp_retcode = PAM_SUCCESS;
+            } else {
+              printf("Warning: No prompt found for: %s\r\n", PAM_MSG_MEMBER( msgs, ii, msg ));
+              reply[ ii ].resp = strdup( "Unexpected prompt" );
+              reply[ ii ].resp_retcode = PAM_SUCCESS;
+            };
+            break;
+          default :
+            reply[ ii ].resp = strdup("");
+            reply[ ii ].resp_retcode = PAM_SUCCESS;
+        };
+      };
+      *res = reply;
+      iResult = PAM_SUCCESS;
+    };
+  };
+  return iResult;
+};
+
+#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 ErlDrvData start( ErlDrvPort port, char* command ) {
+  printf( "PAM-AUTH driver start\r\n" );
+  #ifndef LINKEDLIBPAM
+    init_libpam();
+  #endif
+  ejabberd_pam_data *d = (ejabberd_pam_data *)driver_alloc( sizeof( ejabberd_pam_data ));
+  d->port = port;
+  set_port_control_flags( port, PORT_CONTROL_FLAG_BINARY );
+  return (ErlDrvData)d;
+};
+
+static void stop( ErlDrvData handle ) {
+  driver_free( (char *)handle );
+  #ifndef LINKEDLIBPAM
+    fin_libpam();
+  #endif
+  printf( "PAM-AUTH driver stop\r\n" );
+};
+
+static int auth_params( char* buf, PromptMatch* head ) {
+  int iSize = 0;
+  int iIndex = 0;
+  int iResult = 0;
+  char* prompt = NULL;
+  char* response = NULL;
+  // Parse the version and tuple header
+  ei_decode_version( buf, &iIndex, &iSize );
+  ei_decode_tuple_header( buf, &iIndex, &iSize );
+  if ( iSize == 2 ) {
+    // The first item in the tuple should be the username
+    DECODE_STRING( buf, &iIndex, response );
+    *head = _newMatch( *head, strdup( "login" ), response );
+
+    // 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, &iIndex, &iSize );
+
+    // 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
+    int ii = 0;
+    for ( ii = 0; ii < iSize; ii++ ) {
+      int iTupleSize = 0;
+      ei_decode_tuple_header( buf, &iIndex, &iTupleSize );
+      if ( iTupleSize != 2 ) {
+        printf( "Incorrect number of arguments in prompt/response tuple(index %d): %d\r\n", ii, iTupleSize );
+      } else {
+        // Decode the prompt from the tuple
+        DECODE_STRING( buf, &iIndex, prompt );
+        DECODE_STRING( buf, &iIndex, response );
+        // Add a new prompt item to track this prompt/response pair
+        *head = _newMatch( *head, prompt, response );
+      };
+    };
+    iResult = 1;
+  };
+  return iResult;
+};
+
+static int auth( PromptMatch head ) {
+  // Setup PAM conversation structure; pass in the prompt match list
+  // so we can process the prompts from PAM
+  int flags = 0; //PAM_SILENT
+  int iResult = 0;
+  pam_handle_t* pam = NULL;
+  
+  char* service = _findMatch( head, "service" );
+  char* username = _findMatch( head, "login" );
+
+  struct pam_conv conv = { pam_conversation, head };
+
+  // Spin up PAM
+  int rc = pam_start( service, username, &conv, &pam );
+  if ( rc == PAM_SUCCESS ) {      
+    // Attempt to authenticate the user
+    rc = pam_authenticate( pam, flags );
+    if ( rc != PAM_SUCCESS )
+      printf("Authentication failed for: %s. Error: %s\r\n", username, pam_strerror( NULL, rc ));
+    else   
+      iResult = 1;
+    pam_end( pam, rc );
+  } else
+    printf("Unable to spin up PAM: %s\r\n", pam_strerror( NULL, rc ));
+  return iResult;
+};
+
+static int control( ErlDrvData drv_data, unsigned int command, char *buf, 
+                    int len, char** rbuf, int rlen ) {
+  int iResult;
+  PromptMatch head = NULL;
+  if (( iResult = auth_params( buf, &head )) != 0 ) {
+    iResult = auth( head );
+    _cleanupMatch( head );
+  };
+  RETURN_INT( iResult );
+};
+
+ErlDrvEntry entry = {
+  NULL,			/* F_PTR init, N/A */
+  start,		/* L_PTR start, called when port is opened */
+  stop,	        	/* 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 */
+  "ejabberd_auth_pam",  /* char *driver_name, the argument to open_port */
+  NULL,			/* F_PTR finish, called when unloaded */
+  NULL,			/* handle */
+  control,		/* F_PTR control, port_command callback */
+  NULL,			/* F_PTR timeout, reserved */
+  NULL			/* F_PTR outputv, reserved */
+};
+
+DRIVER_INIT(ejabberd_auth_pam) /* must match name in driver_entry */
+{
+  return &entry;
+};
Index: pam/ejabberd_auth_pam.erl
===================================================================
--- pam/ejabberd_auth_pam.erl	(revisión: 0)
+++ pam/ejabberd_auth_pam.erl	(revisión: 0)
@@ -0,0 +1,90 @@
+-module(ejabberd_auth_pam).
+-author('').
+
+-export([
+         start/1,
+         plain_password_required/0, 
+         set_password/3, 
+	 try_register/3,
+         remove_user/2,
+         remove_user/3, 
+         get_password/2,
+         get_password_s/2,
+         is_user_exists/2,
+         get_vh_registered_users/1,
+         check_password/3,
+	 check_password/5
+        ]).
+
+start( _Host ) ->
+  case erl_ddll:load_driver( ejabberd:get_so_path(), ejabberd_auth_pam) of
+    ok -> ok;
+    {error, already_loaded} -> ok;
+    Error -> exit({error, could_not_load_driver, Error})
+  end.
+
+plain_password_required() ->
+  true.
+
+check_password( User, Server, Password ) ->
+  PAMService = 
+        case ejabberd_config:get_local_option( { pam_service, Server } ) of
+                undefined -> 
+                    "login";
+                Service -> 
+                    Service
+              end,
+  PAMPrompt = 
+        case ejabberd_config:get_local_option( { pam_prompt_pwd, Server } ) of
+                undefined -> 
+                    "Password:";
+                Prompt -> 
+                    Prompt
+              end,
+  Port = open_port( { spawn, ejabberd_auth_pam }, [ binary ] ),
+  Bin = term_to_binary( { User, [ { PAMPrompt, Password }, { "service", PAMService } ] }),
+  Res = port_control( Port, 1, Bin ),
+  case Res of
+    <<0>> -> false;
+    <<1>> -> true;
+    _ -> undefined
+  end.
+
+check_password( User, Server, Password,  _StreamID, _Digest ) ->
+  check_password( User, Server,  Password ).
+
+set_password( _User, _Server, _Password ) ->
+  { error, not_allowed }.
+
+try_register( _User, _Server, _Password ) ->
+  { error, not_allowed }.
+
+remove_user( _User, _Server ) ->
+  { error, not_allowed }.
+
+remove_user( _User, _Server, _Password ) ->
+  { error, not_allowed }.
+    
+get_vh_registered_users( _Server ) ->
+  [].
+    
+is_user_exists( _User, _Server ) ->
+  {ok,FileBin}=file:read_file("/etc/passwd"),
+  Tmp=binary_to_list(FileBin),
+  FileStr=string:concat("\n", Tmp),
+
+  Tmp2=string:concat("\n", _User),
+  RegExp=string:concat(Tmp2, ":"),
+
+  case regexp:match(FileStr, RegExp) of
+    {match, _, _}  ->
+        true;
+    _ ->
+        false
+  end.
+
+get_password( _User, _Server ) ->
+  false.
+
+get_password_s( _User, _Server ) ->
+  "".
Index: aclocal.m4
===================================================================
--- aclocal.m4	(revisión: 705)
+++ aclocal.m4	(copia de trabajo)
@@ -292,3 +292,32 @@
 fi
 ])
 dnl <openssl/>
+
+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
+])
