1 Source: http://ejabberd.jabber.ru/files/contributions/ejabberd_auth_pam.diff
4 ===================================================================
5 --- Makefile.in (revisión: 705)
6 +++ Makefile.in (copia de trabajo)
11 -SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
12 +SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ @pam@
13 ERLSHLIBS = expat_erl.so
14 SOURCES = $(wildcard *.erl)
15 BEAMS = $(SOURCES:.erl=.beam)
16 Index: ejabberd.cfg.example
17 ===================================================================
18 --- ejabberd.cfg.example (revisión: 705)
19 +++ ejabberd.cfg.example (copia de trabajo)
22 %{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
24 +% If you want to allow both internal and PAM authentication:
25 +{auth_method, [internal, pam]}.
26 +% Default PAM service "login" can be changed with:
27 +%{pam_service, "login"}.
28 +% Default PAM prompt "Password:" can be changed with:
29 +%{pam_prompt_pwd, "Password:"}.
33 {hosts, ["localhost"]}.
36 ===================================================================
37 --- configure.ac (revisión: 705)
38 +++ configure.ac (copia de trabajo)
48 # Checks for typedefs, structures, and compiler characteristics.
54 +AC_MOD_ENABLE(pam, yes)
55 AC_MOD_ENABLE(mod_pubsub, yes)
56 AC_MOD_ENABLE(mod_irc, yes)
57 AC_MOD_ENABLE(mod_muc, yes)
61 AC_CONFIG_FILES([Makefile
66 Index: pam/Makefile.in
67 ===================================================================
68 --- pam/Makefile.in (revisión: 0)
69 +++ pam/Makefile.in (revisión: 0)
74 +CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@
75 +CPPFLAGS = @CPPFLAGS@
77 +LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@
81 +ERLSHLIBS = ../ejabberd_auth_pam.so
84 +EFLAGS = -I .. -pz ..
86 + $(OUTDIR)/ejabberd_auth_pam.beam
88 +all: $(OBJS) $(ERLSHLIBS)
90 +$(OUTDIR)/%.beam: %.erl
91 + @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
93 +$(ERLSHLIBS): ../%.so: %.c
94 + $(CC) -Wall $(CFLAGS) $(LDFLAGS) \
95 + $(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
99 + rm -f $(OBJS) $(ERLSHLIBS)
106 Index: pam/ejabberd_auth_pam.c
107 ===================================================================
108 --- pam/ejabberd_auth_pam.c (revisión: 0)
109 +++ pam/ejabberd_auth_pam.c (revisión: 0)
114 +#include <erl_driver.h>
115 +#include <security/pam_appl.h>
117 +#ifdef LINUX_PAM /* XXX: and OpenPAM? */
118 +# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
120 +# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
124 +#ifndef LINKEDLIBPAM
128 +#define LIBPAMPATH "/usr/lib/libpam.so"
131 +static void *libpam_h = NULL;
133 +void init_libpam() {
134 + if ( libpam_h == NULL ) {
135 + libpam_h = dlopen( LIBPAMPATH, RTLD_GLOBAL | RTLD_NOW );
136 + printf( "Open: %s\r\n", LIBPAMPATH );
141 + if ( libpam_h != NULL ) {
142 + dlclose( libpam_h );
144 + printf( "Close: %s\r\n", LIBPAMPATH );
150 +typedef struct _PromptMatch {
151 + struct _PromptMatch* next;
158 +} ejabberd_pam_data;
160 +static PromptMatch _newMatch( PromptMatch list, char* prompt, char* response ) {
161 + PromptMatch result = ( PromptMatch)malloc( sizeof( struct _PromptMatch ));
162 + bzero( result, sizeof( struct _PromptMatch ));
163 + result->prompt = prompt;
164 + result->response = response;
165 + result->next = list;
169 +static char* _findMatch( PromptMatch list, const char* prompt ) {
170 + PromptMatch cur = list;
171 + while ( cur != NULL ) {
172 + if ( strstr( prompt, cur->prompt ) != 0 )
173 + return cur->response;
179 +static void _cleanupMatch( PromptMatch list ) {
180 + PromptMatch cur = list;
182 + while ( cur != NULL ) {
184 + free( cur->prompt );
185 + free( cur->response );
191 +static int pam_conversation( int msgcnt, const struct pam_message** msgs, struct pam_response** res, void* arg ) {
192 + int iResult = PAM_CONV_ERR;
193 + if (( msgcnt > 0 ) || ( msgcnt <= PAM_MAX_NUM_MSG )) {
194 + // Allocate a response for each message
195 + int iSizeResponse = sizeof( struct pam_response ) * msgcnt;
196 + struct pam_response *reply = (struct pam_response *)malloc( iSizeResponse );
197 + if ( reply != NULL ) {
198 + bzero( reply, iSizeResponse );
199 + // Walk each message from PAM and lookup the response corresponding to each prompt
202 + for ( ii = 0; ii < msgcnt; ii++ ) {
203 + printf( "prompt(index %d)(style %d): %s\r\n", ii, PAM_MSG_MEMBER( msgs, ii, msg_style ), PAM_MSG_MEMBER( msgs, ii, msg ));
204 + switch ( PAM_MSG_MEMBER( msgs, ii, msg_style )) {
205 + case PAM_PROMPT_ECHO_ON :
206 + case PAM_PROMPT_ECHO_OFF :
207 + // Find the response for the current prompt; if none is found, warn
208 + response = _findMatch( (PromptMatch)arg, PAM_MSG_MEMBER( msgs, ii, msg ));
209 + if ( response != NULL ) {
210 + // Copy password into response
211 + reply[ ii ].resp = strdup( response );
212 + reply[ ii ].resp_retcode = PAM_SUCCESS;
214 + printf("Warning: No prompt found for: %s\r\n", PAM_MSG_MEMBER( msgs, ii, msg ));
215 + reply[ ii ].resp = strdup( "Unexpected prompt" );
216 + reply[ ii ].resp_retcode = PAM_SUCCESS;
220 + reply[ ii ].resp = strdup("");
221 + reply[ ii ].resp_retcode = PAM_SUCCESS;
225 + iResult = PAM_SUCCESS;
231 +#define DECODE_STRING(buffer, index, result) \
233 + int tmp = 0; int size = 0; \
234 + ei_get_type(buffer, index, &tmp, &size); \
235 + result = malloc(size + 1); \
236 + ei_decode_string(buffer, index, result); \
239 +#define RETURN_INT(value) \
241 + ErlDrvBinary* b = driver_alloc_binary(1); \
243 + b->orig_bytes[0] = value; \
244 + *rbuf = (char*)b; \
249 +static ErlDrvData start( ErlDrvPort port, char* command ) {
250 + printf( "PAM-AUTH driver start\r\n" );
251 + #ifndef LINKEDLIBPAM
254 + ejabberd_pam_data *d = (ejabberd_pam_data *)driver_alloc( sizeof( ejabberd_pam_data ));
256 + set_port_control_flags( port, PORT_CONTROL_FLAG_BINARY );
257 + return (ErlDrvData)d;
260 +static void stop( ErlDrvData handle ) {
261 + driver_free( (char *)handle );
262 + #ifndef LINKEDLIBPAM
265 + printf( "PAM-AUTH driver stop\r\n" );
268 +static int auth_params( char* buf, PromptMatch* head ) {
272 + char* prompt = NULL;
273 + char* response = NULL;
274 + // Parse the version and tuple header
275 + ei_decode_version( buf, &iIndex, &iSize );
276 + ei_decode_tuple_header( buf, &iIndex, &iSize );
277 + if ( iSize == 2 ) {
278 + // The first item in the tuple should be the username
279 + DECODE_STRING( buf, &iIndex, response );
280 + *head = _newMatch( *head, strdup( "login" ), response );
282 + // The second item in the tuple should be a list of tuples
283 + // Each tuple in the list consists of the expected prompt string
284 + // and the value to return at that prompt
285 + ei_decode_list_header( buf, &iIndex, &iSize );
287 + // Loop over the list and pull out each prompt/response tuple and
288 + // store it into a PromptItem list that will get processed in
289 + // the PAM conversation
291 + for ( ii = 0; ii < iSize; ii++ ) {
292 + int iTupleSize = 0;
293 + ei_decode_tuple_header( buf, &iIndex, &iTupleSize );
294 + if ( iTupleSize != 2 ) {
295 + printf( "Incorrect number of arguments in prompt/response tuple(index %d): %d\r\n", ii, iTupleSize );
297 + // Decode the prompt from the tuple
298 + DECODE_STRING( buf, &iIndex, prompt );
299 + DECODE_STRING( buf, &iIndex, response );
300 + // Add a new prompt item to track this prompt/response pair
301 + *head = _newMatch( *head, prompt, response );
309 +static int auth( PromptMatch head ) {
310 + // Setup PAM conversation structure; pass in the prompt match list
311 + // so we can process the prompts from PAM
312 + int flags = 0; //PAM_SILENT
314 + pam_handle_t* pam = NULL;
316 + char* service = _findMatch( head, "service" );
317 + char* username = _findMatch( head, "login" );
319 + struct pam_conv conv = { pam_conversation, head };
322 + int rc = pam_start( service, username, &conv, &pam );
323 + if ( rc == PAM_SUCCESS ) {
324 + // Attempt to authenticate the user
325 + rc = pam_authenticate( pam, flags );
326 + if ( rc != PAM_SUCCESS )
327 + printf("Authentication failed for: %s. Error: %s\r\n", username, pam_strerror( NULL, rc ));
330 + pam_end( pam, rc );
332 + printf("Unable to spin up PAM: %s\r\n", pam_strerror( NULL, rc ));
336 +static int control( ErlDrvData drv_data, unsigned int command, char *buf,
337 + int len, char** rbuf, int rlen ) {
339 + PromptMatch head = NULL;
340 + if (( iResult = auth_params( buf, &head )) != 0 ) {
341 + iResult = auth( head );
342 + _cleanupMatch( head );
344 + RETURN_INT( iResult );
347 +ErlDrvEntry entry = {
348 + NULL, /* F_PTR init, N/A */
349 + start, /* L_PTR start, called when port is opened */
350 + stop, /* F_PTR stop, called when port is closed */
351 + NULL, /* F_PTR output, called when erlang has sent */
352 + NULL, /* F_PTR ready_input, called when input descriptor ready */
353 + NULL, /* F_PTR ready_output, called when output descriptor ready */
354 + "ejabberd_auth_pam", /* char *driver_name, the argument to open_port */
355 + NULL, /* F_PTR finish, called when unloaded */
357 + control, /* F_PTR control, port_command callback */
358 + NULL, /* F_PTR timeout, reserved */
359 + NULL /* F_PTR outputv, reserved */
362 +DRIVER_INIT(ejabberd_auth_pam) /* must match name in driver_entry */
366 Index: pam/ejabberd_auth_pam.erl
367 ===================================================================
368 --- pam/ejabberd_auth_pam.erl (revisión: 0)
369 +++ pam/ejabberd_auth_pam.erl (revisión: 0)
371 +-module(ejabberd_auth_pam).
376 + plain_password_required/0,
384 + get_vh_registered_users/1,
390 + case erl_ddll:load_driver( ejabberd:get_so_path(), ejabberd_auth_pam) of
392 + {error, already_loaded} -> ok;
393 + Error -> exit({error, could_not_load_driver, Error})
396 +plain_password_required() ->
399 +check_password( User, Server, Password ) ->
401 + case ejabberd_config:get_local_option( { pam_service, Server } ) of
408 + case ejabberd_config:get_local_option( { pam_prompt_pwd, Server } ) of
414 + Port = open_port( { spawn, ejabberd_auth_pam }, [ binary ] ),
415 + Bin = term_to_binary( { User, [ { PAMPrompt, Password }, { "service", PAMService } ] }),
416 + Res = port_control( Port, 1, Bin ),
423 +check_password( User, Server, Password, _StreamID, _Digest ) ->
424 + check_password( User, Server, Password ).
426 +set_password( _User, _Server, _Password ) ->
427 + { error, not_allowed }.
429 +try_register( _User, _Server, _Password ) ->
430 + { error, not_allowed }.
432 +remove_user( _User, _Server ) ->
433 + { error, not_allowed }.
435 +remove_user( _User, _Server, _Password ) ->
436 + { error, not_allowed }.
438 +get_vh_registered_users( _Server ) ->
441 +is_user_exists( _User, _Server ) ->
442 + {ok,FileBin}=file:read_file("/etc/passwd"),
443 + Tmp=binary_to_list(FileBin),
444 + FileStr=string:concat("\n", Tmp),
446 + Tmp2=string:concat("\n", _User),
447 + RegExp=string:concat(Tmp2, ":"),
449 + case regexp:match(FileStr, RegExp) of
456 +get_password( _User, _Server ) ->
459 +get_password_s( _User, _Server ) ->
462 ===================================================================
463 --- aclocal.m4 (revisión: 705)
464 +++ aclocal.m4 (copia de trabajo)
470 +AC_DEFUN(AM_WITH_PAM,
471 +[ AC_ARG_WITH(pam, [ --with-pam=PREFIX prefix where PAM is installed ])
475 +if test x"$with_pam" != x; then
476 + PAM_CFLAGS="-I$with_pam/include"
477 + PAM_LIBS="-L$with_pam/lib"
480 +AC_CHECK_LIB(pam, pam_start,
481 + [ PAM_LIBS="$PAM_LIBS -lpam" pam_found=yes ],
485 +if test $pam_found = yes; then
486 + pam_save_CFLAGS="$CFLAGS"
487 + CFLAGS="$PAM_CFLAGS $CFLAGS"
488 + AC_CHECK_HEADERS(security/pam_appl.h, ,$pam_found=no)
489 + if test $pam_found = no; then
490 + AC_MSG_ERROR([Could not find security/pam_appl.h])
492 + CFLAGS=$pam_save_CFLAGS
494 + AC_SUBST(PAM_CFLAGS)