mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-30 16:36:29 +01:00
Support LDAPS with TLS (EJAB-109)(thanks to Thomas Baden, Andy Harb, Sergei Golovan, Anton Podavalov)
SVN Revision: 2099
This commit is contained in:
parent
dc3899dfd9
commit
18ae44f930
@ -1532,12 +1532,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 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 <TT>ejabberd</TT>’s database.
|
If you configure a value, it is stored in <TT>ejabberd</TT>’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 <TT>""</TT> which means ‘anonymous connection’.
|
is <TT>""</TT> which means ‘anonymous connection’.
|
||||||
</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
|
||||||
@ -1592,14 +1595,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’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’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
|
||||||
|
@ -2057,12 +2057,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
|
||||||
@ -2137,8 +2140,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
|
||||||
@ -2148,6 +2153,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
|
||||||
|
@ -234,17 +234,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:
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
servers,
|
servers,
|
||||||
backups,
|
backups,
|
||||||
port,
|
port,
|
||||||
|
encrypt,
|
||||||
dn,
|
dn,
|
||||||
password,
|
password,
|
||||||
base,
|
base,
|
||||||
@ -137,13 +138,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}.
|
||||||
|
|
||||||
%% @spec () -> true
|
%% @spec () -> true
|
||||||
@ -443,8 +446,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
|
||||||
@ -479,6 +487,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,
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
servers,
|
servers,
|
||||||
backups,
|
backups,
|
||||||
port,
|
port,
|
||||||
|
encrypt,
|
||||||
dn,
|
dn,
|
||||||
base,
|
base,
|
||||||
password,
|
password,
|
||||||
@ -183,7 +184,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);
|
||||||
@ -653,11 +655,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,
|
||||||
@ -727,6 +740,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,
|
||||||
|
Loading…
Reference in New Issue
Block a user