Support LDAPS with TLS (EJAB-109)(thanks to Thomas Baden, Andy Harb, Sergei Golovan, Anton Podavalov)

SVN Revision: 2098
This commit is contained in:
Badlop 2009-05-25 17:15:48 +00:00
parent 3ec3e78baa
commit 31aa201ee8
8 changed files with 159 additions and 49 deletions

View File

@ -1568,12 +1568,15 @@ create accounts, change password or edit vCard that is stored in LDAP.</P><P> <A
</P><DL CLASS="description"><DT CLASS="dt-description"> </P><DL CLASS="description"><DT CLASS="dt-description">
<B><TT>ldap_servers</TT></B></DT><DD CLASS="dd-description"> List of IP addresses or DNS names of your <B><TT>ldap_servers</TT></B></DT><DD CLASS="dd-description"> List of IP addresses or DNS names of your
LDAP servers. This option is required. LDAP servers. This option is required.
</DD><DT CLASS="dt-description"><B><TT>ldap_encrypt</TT></B></DT><DD CLASS="dd-description"> Type of connection encryption to the LDAP server.
Allowed values are: <TT>none</TT>, <TT>tls</TT>.
Note that STARTTLS is not supported.
The default value is: <TT>none</TT>.
</DD><DT CLASS="dt-description"><B><TT>ldap_port</TT></B></DT><DD CLASS="dd-description"> Port to connect to your LDAP server. </DD><DT CLASS="dt-description"><B><TT>ldap_port</TT></B></DT><DD CLASS="dd-description"> Port to connect to your LDAP server.
The initial default value is&#XA0;389, so it is used when nothing is set into the The default port is&#XA0;389 if encryption is disabled; and 636 if encryption is enabled.
configuration file.
If you configure a value, it is stored in <TT>ejabberd</TT>&#X2019;s database. If you configure a value, it is stored in <TT>ejabberd</TT>&#X2019;s database.
Then, if you remove that value from the configuration file, Then, if you remove that value from the configuration file,
the value previously stored in the database will be used instead of the default 389. the value previously stored in the database will be used instead of the default port.
</DD><DT CLASS="dt-description"><B><TT>ldap_rootdn</TT></B></DT><DD CLASS="dd-description"> Bind DN. The default value </DD><DT CLASS="dt-description"><B><TT>ldap_rootdn</TT></B></DT><DD CLASS="dd-description"> Bind DN. The default value
is&#XA0;<TT>""</TT> which means &#X2018;anonymous connection&#X2019;. is&#XA0;<TT>""</TT> which means &#X2018;anonymous connection&#X2019;.
</DD><DT CLASS="dt-description"><B><TT>ldap_password</TT></B></DT><DD CLASS="dd-description"> Bind password. The default </DD><DT CLASS="dt-description"><B><TT>ldap_password</TT></B></DT><DD CLASS="dd-description"> Bind password. The default
@ -1628,14 +1631,18 @@ Example values:
<H5 CLASS="paragraph"><!--SEC ANCHOR --><A HREF="#ldapcommonexample">Common example</A></H5><!--SEC END --><P> <A NAME="ldapcommonexample"></A> </P><P>Let&#X2019;s say <TT>ldap.example.org</TT> is the name of our LDAP server. We have <H5 CLASS="paragraph"><!--SEC ANCHOR --><A HREF="#ldapcommonexample">Common example</A></H5><!--SEC END --><P> <A NAME="ldapcommonexample"></A> </P><P>Let&#X2019;s say <TT>ldap.example.org</TT> is the name of our LDAP server. We have
users with their passwords in <TT>"ou=Users,dc=example,dc=org"</TT> directory. users with their passwords in <TT>"ou=Users,dc=example,dc=org"</TT> directory.
Also we have addressbook, which contains users emails and their additional Also we have addressbook, which contains users emails and their additional
infos in <TT>"ou=AddressBook,dc=example,dc=org"</TT> directory. Corresponding infos in <TT>"ou=AddressBook,dc=example,dc=org"</TT> directory.
authentication section should looks like this:</P><PRE CLASS="verbatim">%% Authentication method The connection to the LDAP server is encrypted using TLS,
and using the custom port 6123.
Corresponding authentication section should looks like this:</P><PRE CLASS="verbatim">%% Authentication method
{auth_method, ldap}. {auth_method, ldap}.
%% DNS name of our LDAP server %% DNS name of our LDAP server
{ldap_servers, ["ldap.example.org"]}. {ldap_servers, ["ldap.example.org"]}.
%% Bind to LDAP server as "cn=Manager,dc=example,dc=org" with password "secret" %% Bind to LDAP server as "cn=Manager,dc=example,dc=org" with password "secret"
{ldap_rootdn, "cn=Manager,dc=example,dc=org"}. {ldap_rootdn, "cn=Manager,dc=example,dc=org"}.
{ldap_password, "secret"}. {ldap_password, "secret"}.
{ldap_encrypt, tls}.
{ldap_port, 6123}.
%% Define the user's base %% Define the user's base
{ldap_base, "ou=Users,dc=example,dc=org"}. {ldap_base, "ou=Users,dc=example,dc=org"}.
%% We want to authorize users from 'shadowAccount' object class only %% We want to authorize users from 'shadowAccount' object class only

View File

@ -2105,12 +2105,15 @@ Parameters:
\begin{description} \begin{description}
\titem{ldap\_servers} \ind{options!ldap\_server}List of IP addresses or DNS names of your \titem{ldap\_servers} \ind{options!ldap\_server}List of IP addresses or DNS names of your
LDAP servers. This option is required. LDAP servers. This option is required.
\titem{ldap\_encrypt} \ind{options!ldap\_encrypt}Type of connection encryption to the LDAP server.
Allowed values are: \term{none}, \term{tls}.
Note that STARTTLS is not supported.
The default value is: \term{none}.
\titem{ldap\_port} \ind{options!ldap\_port}Port to connect to your LDAP server. \titem{ldap\_port} \ind{options!ldap\_port}Port to connect to your LDAP server.
The initial default value is~389, so it is used when nothing is set into the The default port is~389 if encryption is disabled; and 636 if encryption is enabled.
configuration file.
If you configure a value, it is stored in \ejabberd{}'s database. If you configure a value, it is stored in \ejabberd{}'s database.
Then, if you remove that value from the configuration file, Then, if you remove that value from the configuration file,
the value previously stored in the database will be used instead of the default 389. the value previously stored in the database will be used instead of the default port.
\titem{ldap\_rootdn} \ind{options!ldap\_rootdn}Bind DN. The default value \titem{ldap\_rootdn} \ind{options!ldap\_rootdn}Bind DN. The default value
is~\term{""} which means `anonymous connection'. is~\term{""} which means `anonymous connection'.
\titem{ldap\_password} \ind{options!ldap\_password}Bind password. The default \titem{ldap\_password} \ind{options!ldap\_password}Bind password. The default
@ -2185,8 +2188,10 @@ You can authenticate users against an LDAP directory. Available options are:
Let's say \term{ldap.example.org} is the name of our LDAP server. We have Let's say \term{ldap.example.org} is the name of our LDAP server. We have
users with their passwords in \term{"ou=Users,dc=example,dc=org"} directory. users with their passwords in \term{"ou=Users,dc=example,dc=org"} directory.
Also we have addressbook, which contains users emails and their additional Also we have addressbook, which contains users emails and their additional
infos in \term{"ou=AddressBook,dc=example,dc=org"} directory. Corresponding infos in \term{"ou=AddressBook,dc=example,dc=org"} directory.
authentication section should looks like this: The connection to the LDAP server is encrypted using TLS,
and using the custom port 6123.
Corresponding authentication section should looks like this:
\begin{verbatim} \begin{verbatim}
%% Authentication method %% Authentication method
@ -2196,6 +2201,8 @@ authentication section should looks like this:
%% Bind to LDAP server as "cn=Manager,dc=example,dc=org" with password "secret" %% Bind to LDAP server as "cn=Manager,dc=example,dc=org" with password "secret"
{ldap_rootdn, "cn=Manager,dc=example,dc=org"}. {ldap_rootdn, "cn=Manager,dc=example,dc=org"}.
{ldap_password, "secret"}. {ldap_password, "secret"}.
{ldap_encrypt, tls}.
{ldap_port, 6123}.
%% Define the user's base %% Define the user's base
{ldap_base, "ou=Users,dc=example,dc=org"}. {ldap_base, "ou=Users,dc=example,dc=org"}.
%% We want to authorize users from 'shadowAccount' object class only %% We want to authorize users from 'shadowAccount' object class only

View File

@ -235,17 +235,28 @@
%% List of LDAP servers: %% List of LDAP servers:
%%{ldap_servers, ["localhost"]}. %%{ldap_servers, ["localhost"]}.
%% %%
%% LDAP attribute that holds user ID: %% Encryption of connection to LDAP servers:
%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. %%{ldap_encrypt, none}.
%%{ldap_encrypt, tls}.
%% %%
%% Search base of LDAP directory: %% Port connect to LDAP servers:
%%{ldap_base, "dc=example,dc=com"}. %%{ldap_port, 389}.
%%{ldap_port, 636}.
%% %%
%% LDAP manager: %% LDAP manager:
%%{ldap_rootdn, "dc=example,dc=com"}. %%{ldap_rootdn, "dc=example,dc=com"}.
%% %%
%% Password to LDAP manager: %% Password to LDAP manager:
%%{ldap_password, "******"}. %%{ldap_password, "******"}.
%%
%% Search base of LDAP directory:
%%{ldap_base, "dc=example,dc=com"}.
%%
%% LDAP attribute that holds user ID:
%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}.
%%
%% LDAP filter:
%%{ldap_filter, "(objectClass=shadowAccount)"}.
%% %%
%% Anonymous login support: %% Anonymous login support:

View File

@ -66,6 +66,7 @@
servers, servers,
backups, backups,
port, port,
encrypt,
dn, dn,
password, password,
base, base,
@ -122,13 +123,15 @@ init(Host) ->
State#state.backups, State#state.backups,
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password), State#state.password,
State#state.encrypt),
eldap_pool:start_link(State#state.bind_eldap_id, eldap_pool:start_link(State#state.bind_eldap_id,
State#state.servers, State#state.servers,
State#state.backups, State#state.backups,
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password), State#state.password,
State#state.encrypt),
{ok, State}. {ok, State}.
plain_password_required() -> plain_password_required() ->
@ -354,8 +357,13 @@ parse_options(Host) ->
undefined -> []; undefined -> [];
Backups -> Backups Backups -> Backups
end, end,
LDAPEncrypt = ejabberd_config:get_local_option({ldap_encrypt, Host}),
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
undefined -> 389; undefined -> case LDAPEncrypt of
tls -> ?LDAPS_PORT;
starttls -> ?LDAP_PORT;
_ -> ?LDAP_PORT
end;
P -> P P -> P
end, end,
RootDN = case ejabberd_config:get_local_option({ldap_rootdn, Host}) of RootDN = case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
@ -390,6 +398,7 @@ parse_options(Host) ->
servers = LDAPServers, servers = LDAPServers,
backups = LDAPBackups, backups = LDAPBackups,
port = LDAPPort, port = LDAPPort,
encrypt = LDAPEncrypt,
dn = RootDN, dn = RootDN,
password = Password, password = Password,
base = LDAPBase, base = LDAPBase,

View File

@ -42,6 +42,12 @@
%%% Modified by Mickael Remond <mremond@process-one.net> %%% Modified by Mickael Remond <mremond@process-one.net>
%%% Now use ejabberd log mechanism %%% Now use ejabberd log mechanism
%%% Modified by:
%%% Thomas Baden <roo@ham9.net> 2008 April 6th
%%% Andy Harb <Ahmad.N.Abou-Harb@jpl.nasa.gov> 2008 April 28th
%%% Anton Podavalov <a.podavalov@gmail.com> 2009 February 22th
%%% Added LDAPS support, modeled off jungerl eldap.erl version.
%%% NOTICE: STARTTLS is not supported.
%%% -------------------------------------------------------------------- %%% --------------------------------------------------------------------
-vc('$Id$ '). -vc('$Id$ ').
@ -61,7 +67,7 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
%% External exports %% External exports
-export([start_link/1, start_link/5]). -export([start_link/1, start_link/6]).
-export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1, -export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2, equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
@ -94,10 +100,17 @@
%% Grace period after "soft" LDAP bind errors: %% Grace period after "soft" LDAP bind errors:
-define(GRACEFUL_RETRY_TIMEOUT, 5000). -define(GRACEFUL_RETRY_TIMEOUT, 5000).
-define(SUPPORTEDEXTENSION, "1.3.6.1.4.1.1466.101.120.7").
-define(SUPPORTEDEXTENSIONSYNTAX, "1.3.6.1.4.1.1466.115.121.1.38").
-define(STARTTLS, "1.3.6.1.4.1.1466.20037").
-record(eldap, {version = ?LDAP_VERSION, -record(eldap, {version = ?LDAP_VERSION,
hosts, % Possible hosts running LDAP servers hosts, % Possible hosts running LDAP servers
host = null, % Connected Host LDAP server host = null, % Connected Host LDAP server
port = 389, % The LDAP server port port = 389, % The LDAP server port
sockmod, % SockMod (gen_tcp|tls)
tls = none, % LDAP/LDAPS (none|starttls|tls)
tls_options = [],
fd = null, % Socket filedescriptor. fd = null, % Socket filedescriptor.
rootdn = "", % Name of the entry to bind as rootdn = "", % Name of the entry to bind as
passwd, % Password for (above) entry passwd, % Password for (above) entry
@ -114,9 +127,9 @@ start_link(Name) ->
Reg_name = list_to_atom("eldap_" ++ Name), Reg_name = list_to_atom("eldap_" ++ Name),
gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []). gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
start_link(Name, Hosts, Port, Rootdn, Passwd) -> start_link(Name, Hosts, Port, Rootdn, Passwd, Encrypt) ->
Reg_name = list_to_atom("eldap_" ++ Name), Reg_name = list_to_atom("eldap_" ++ Name),
gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd}, []). gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd, Encrypt}, []).
%%% -------------------------------------------------------------------- %%% --------------------------------------------------------------------
%%% Get status of connection. %%% Get status of connection.
@ -380,16 +393,34 @@ get_handle(Name) when is_list(Name) -> list_to_atom("eldap_" ++ Name).
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
init([]) -> init([]) ->
case get_config() of case get_config() of
{ok, Hosts, Rootdn, Passwd} -> {ok, Hosts, Rootdn, Passwd, Encrypt} ->
init({Hosts, Rootdn, Passwd}); init({Hosts, Rootdn, Passwd, Encrypt});
{error, Reason} -> {error, Reason} ->
{stop, Reason} {stop, Reason}
end; end;
init({Hosts, Port, Rootdn, Passwd}) -> init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
catch ssl:start(),
{X1,X2,X3} = erlang:now(),
ssl:seed(integer_to_list(X1) ++ integer_to_list(X2) ++ integer_to_list(X3)),
PortTemp = case Port of
undefined ->
case Encrypt of
tls ->
?LDAPS_PORT;
starttls ->
?LDAP_PORT;
_ ->
?LDAP_PORT
end;
PT -> PT
end,
TLSOpts = [verify_none],
{ok, connecting, #eldap{hosts = Hosts, {ok, connecting, #eldap{hosts = Hosts,
port = Port, port = PortTemp,
rootdn = Rootdn, rootdn = Rootdn,
passwd = Passwd, passwd = Passwd,
tls = Encrypt,
tls_options = TLSOpts,
id = 0, id = 0,
dict = dict:new(), dict = dict:new(),
req_q = queue:new()}, 0}. req_q = queue:new()}, 0}.
@ -438,7 +469,7 @@ active(Event, From, S) ->
%% {stop, Reason, NewStateData} %% {stop, Reason, NewStateData}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_event(close, _StateName, S) -> handle_event(close, _StateName, S) ->
catch gen_tcp:close(S#eldap.fd), catch (S#eldap.sockmod):close(S#eldap.fd),
{stop, normal, S}; {stop, normal, S};
handle_event(_Event, StateName, S) -> handle_event(_Event, StateName, S) ->
@ -467,11 +498,13 @@ handle_sync_event(_Event, _From, StateName, S) ->
%% %%
%% Packets arriving in various states %% Packets arriving in various states
%% %%
handle_info({tcp, _Socket, Data}, connecting, S) -> handle_info({Tag, _Socket, Data}, connecting, S)
when Tag == tcp; Tag == ssl ->
?DEBUG("tcp packet received when disconnected!~n~p", [Data]), ?DEBUG("tcp packet received when disconnected!~n~p", [Data]),
{next_state, connecting, S}; {next_state, connecting, S};
handle_info({tcp, _Socket, Data}, wait_bind_response, S) -> handle_info({Tag, _Socket, Data}, wait_bind_response, S)
when Tag == tcp; Tag == ssl ->
cancel_timer(S#eldap.bind_timer), cancel_timer(S#eldap.bind_timer),
case catch recvd_wait_bind_response(Data, S) of case catch recvd_wait_bind_response(Data, S) of
bound -> bound ->
@ -487,8 +520,9 @@ handle_info({tcp, _Socket, Data}, wait_bind_response, S) ->
{next_state, connecting, close_and_retry(S)} {next_state, connecting, close_and_retry(S)}
end; end;
handle_info({tcp, _Socket, Data}, StateName, S) handle_info({Tag, _Socket, Data}, StateName, S)
when StateName == active orelse StateName == active_bind -> when (StateName == active orelse StateName == active_bind) andalso
(Tag == tcp orelse Tag == ssl) ->
case catch recvd_packet(Data, S) of case catch recvd_packet(Data, S) of
{response, Response, RequestType} -> {response, Response, RequestType} ->
NewS = case Response of NewS = case Response of
@ -509,12 +543,14 @@ handle_info({tcp, _Socket, Data}, StateName, S)
{next_state, StateName, S} {next_state, StateName, S}
end; end;
handle_info({tcp_closed, _Socket}, Fsm_state, S) -> handle_info({Tag, _Socket}, Fsm_state, S)
when Tag == tcp_closed; Tag == ssl_closed ->
?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn State: ~p", ?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn State: ~p",
[S#eldap.host, S#eldap.port ,Fsm_state]), [S#eldap.host, S#eldap.port ,Fsm_state]),
{next_state, connecting, close_and_retry(S)}; {next_state, connecting, close_and_retry(S)};
handle_info({tcp_error, _Socket, Reason}, Fsm_state, S) -> handle_info({Tag, _Socket, Reason}, Fsm_state, S)
when Tag == tcp_error; Tag == ssl_error ->
?DEBUG("eldap received tcp_error: ~p~nIn State: ~p", [Reason, Fsm_state]), ?DEBUG("eldap received tcp_error: ~p~nIn State: ~p", [Reason, Fsm_state]),
{next_state, connecting, close_and_retry(S)}; {next_state, connecting, close_and_retry(S)};
@ -597,7 +633,7 @@ send_command(Command, From, S) ->
protocolOp = {Name, Request}}, protocolOp = {Name, Request}},
?DEBUG("~p~n",[{Name, Request}]), ?DEBUG("~p~n",[{Name, Request}]),
{ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), {ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message),
case gen_tcp:send(S#eldap.fd, Bytes) of case (S#eldap.sockmod):send(S#eldap.fd, Bytes) of
ok -> ok ->
Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}), Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}),
New_dict = dict:store(Id, [{Timer, Command, From, Name}], S#eldap.dict), New_dict = dict:store(Id, [{Timer, Command, From, Name}], S#eldap.dict),
@ -796,7 +832,7 @@ check_tag(Data) ->
end. end.
close_and_retry(S, Timeout) -> close_and_retry(S, Timeout) ->
catch gen_tcp:close(S#eldap.fd), catch (S#eldap.sockmod):close(S#eldap.fd),
Queue = dict:fold( Queue = dict:fold(
fun(_Id, [{Timer, Command, From, _Name}|_], Q) -> fun(_Id, [{Timer, Command, From, _Name}|_], Q) ->
cancel_timer(Timer), cancel_timer(Timer),
@ -863,16 +899,28 @@ polish([], Res, Ref) ->
%%----------------------------------------------------------------------- %%-----------------------------------------------------------------------
connect_bind(S) -> connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts), Host = next_host(S#eldap.host, S#eldap.hosts),
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary],
?INFO_MSG("LDAP connection on ~s:~p", [Host, S#eldap.port]), ?INFO_MSG("LDAP connection on ~s:~p", [Host, S#eldap.port]),
case gen_tcp:connect(Host, S#eldap.port, TcpOpts) of SocketData = case S#eldap.tls of
tls ->
SockMod = ssl,
SslOpts = [{packet, asn1}, {active, true}, {keepalive, true},
binary],
ssl:connect(Host, S#eldap.port, SslOpts);
%% starttls -> %% TODO: Implement STARTTLS;
_ ->
SockMod = gen_tcp,
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary],
gen_tcp:connect(Host, S#eldap.port, TcpOpts)
end,
case SocketData of
{ok, Socket} -> {ok, Socket} ->
case bind_request(Socket, S) of case bind_request(Socket, S#eldap{sockmod = SockMod}) of
{ok, NewS} -> {ok, NewS} ->
Timer = erlang:start_timer(?BIND_TIMEOUT, self(), Timer = erlang:start_timer(?BIND_TIMEOUT, self(),
{timeout, bind_timeout}), {timeout, bind_timeout}),
{ok, wait_bind_response, NewS#eldap{fd = Socket, {ok, wait_bind_response, NewS#eldap{fd = Socket,
sockmod = SockMod,
host = Host, host = Host,
bind_timer = Timer}}; bind_timer = Timer}};
{error, Reason} -> {error, Reason} ->
@ -896,7 +944,7 @@ bind_request(Socket, S) ->
protocolOp = {bindRequest, Req}}, protocolOp = {bindRequest, Req}},
?DEBUG("Bind Request Message:~p~n",[Message]), ?DEBUG("Bind Request Message:~p~n",[Message]),
{ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), {ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message),
case gen_tcp:send(Socket, Bytes) of case (S#eldap.sockmod):send(Socket, Bytes) of
ok -> {ok, S#eldap{id = Id}}; ok -> {ok, S#eldap{id = Id}};
Error -> Error Error -> Error
end. end.
@ -970,8 +1018,8 @@ get_config() ->
case file:consult(File) of case file:consult(File) of
{ok, Entries} -> {ok, Entries} ->
case catch parse(Entries) of case catch parse(Entries) of
{ok, Hosts, Port, Rootdn, Passwd} -> {ok, Hosts, Port, Rootdn, Passwd, Encrypt} ->
{ok, Hosts, Port, Rootdn, Passwd}; {ok, Hosts, Port, Rootdn, Passwd, Encrypt};
{error, Reason} -> {error, Reason} ->
{error, Reason}; {error, Reason};
{'EXIT', Reason} -> {'EXIT', Reason} ->
@ -986,7 +1034,8 @@ parse(Entries) ->
get_hosts(host, Entries), get_hosts(host, Entries),
get_integer(port, Entries), get_integer(port, Entries),
get_list(rootdn, Entries), get_list(rootdn, Entries),
get_list(passwd, Entries)}. get_list(passwd, Entries),
get_atom(encrypt, Entries)}.
get_integer(Key, List) -> get_integer(Key, List) ->
case lists:keysearch(Key, 1, List) of case lists:keysearch(Key, 1, List) of
@ -1008,6 +1057,16 @@ get_list(Key, List) ->
throw({error, "No Entry in Config for " ++ atom_to_list(Key)}) throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
end. end.
get_atom(Key, List) ->
case lists:keysearch(Key, 1, List) of
{value, {Key, Value}} when atom(Value) ->
Value;
{value, {Key, _Value}} ->
throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
false ->
throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
end.
get_hosts(Key, List) -> get_hosts(Key, List) ->
lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A), lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A),
is_integer(B), is_integer(B),

View File

@ -19,6 +19,9 @@
%%% %%%
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
-define(LDAP_PORT, 389).
-define(LDAPS_PORT, 636).
-record(eldap_search, {scope = wholeSubtree, -record(eldap_search, {scope = wholeSubtree,
base = [], base = [],
filter, filter,

View File

@ -29,7 +29,7 @@
%% API %% API
-export([ -export([
start_link/6, start_link/7,
bind/3, bind/3,
search/2 search/2
]). ]).
@ -45,12 +45,12 @@ bind(PoolName, DN, Passwd) ->
search(PoolName, Opts) -> search(PoolName, Opts) ->
do_request(PoolName, {search, [Opts]}). do_request(PoolName, {search, [Opts]}).
start_link(Name, Hosts, Backups, Port, Rootdn, Passwd) -> start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Encrypt) ->
PoolName = make_id(Name), PoolName = make_id(Name),
pg2:create(PoolName), pg2:create(PoolName),
lists:foreach(fun(Host) -> lists:foreach(fun(Host) ->
ID = erlang:ref_to_list(make_ref()), ID = erlang:ref_to_list(make_ref()),
case catch eldap:start_link(ID, [Host|Backups], Port, Rootdn, Passwd) of case catch eldap:start_link(ID, [Host|Backups], Port, Rootdn, Passwd, Encrypt) of
{ok, Pid} -> {ok, Pid} ->
pg2:join(PoolName, Pid); pg2:join(PoolName, Pid);
_ -> _ ->

View File

@ -62,6 +62,7 @@
servers, servers,
backups, backups,
port, port,
encrypt,
dn, dn,
base, base,
password, password,
@ -179,7 +180,8 @@ init([Host, Opts]) ->
State#state.backups, State#state.backups,
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password), State#state.password,
State#state.encrypt),
case State#state.search of case State#state.search of
true -> true ->
ejabberd_router:register_route(State#state.myhost); ejabberd_router:register_route(State#state.myhost);
@ -673,11 +675,22 @@ parse_options(Host, Opts) ->
ejabberd_config:get_local_option({ldap_servers, Host}); ejabberd_config:get_local_option({ldap_servers, Host});
Backups -> Backups Backups -> Backups
end, end,
LDAPPort = case gen_mod:get_opt(ldap_port, Opts, undefined) of LDAPEncrypt = case gen_mod:get_opt(ldap_encrypt, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_encrypt, Host});
E -> E
end,
LDAPPortTemp = case gen_mod:get_opt(ldap_port, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_port, Host});
PT -> PT
end,
LDAPPort = case LDAPPortTemp of
undefined -> undefined ->
case ejabberd_config:get_local_option({ldap_port, Host}) of case LDAPEncrypt of
undefined -> 389; tls -> ?LDAPS_PORT;
P -> P starttls -> ?LDAP_PORT;
_ -> ?LDAP_PORT
end; end;
P -> P P -> P
end, end,
@ -747,6 +760,7 @@ parse_options(Host, Opts) ->
servers = LDAPServers, servers = LDAPServers,
backups = LDAPBackups, backups = LDAPBackups,
port = LDAPPort, port = LDAPPort,
encrypt = LDAPEncrypt,
dn = RootDN, dn = RootDN,
base = LDAPBase, base = LDAPBase,
password = Password, password = Password,