Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2010-04-15 19:20:16 +0400
committerBadlop <badlop@process-one.net>2010-07-23 03:23:35 +0400
commitaa791ad0c4d640f111d97e770ddd0c6328472593 (patch)
tree560a140fc3a3caa6c57f92eb60e82845105b575a /src
parent17fc992ba9881e9db270db333c7e8d8282a19858 (diff)
Support SASL GSSAPI authentication (thanks to Mikael Magnusson)(EJAB-831)
Diffstat (limited to 'src')
-rw-r--r--src/cyrsasl.erl36
-rw-r--r--src/cyrsasl.hrl15
-rw-r--r--src/cyrsasl_anonymous.erl12
-rw-r--r--src/cyrsasl_digest.erl12
-rw-r--r--src/cyrsasl_gssapi.erl167
-rw-r--r--src/cyrsasl_plain.erl10
-rw-r--r--src/ejabberd_c2s.erl14
-rw-r--r--src/ejabberd_socket.erl19
8 files changed, 244 insertions, 41 deletions
diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
index 312d32bed..f0737d352 100644
--- a/src/cyrsasl.erl
+++ b/src/cyrsasl.erl
@@ -30,10 +30,12 @@
-export([start/0,
register_mechanism/3,
listmech/1,
- server_new/7,
+ server_new/8,
server_start/3,
server_step/2]).
+-include("cyrsasl.hrl").
+
%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain}
%% Mechanism = string()
%% Module = atom()
@@ -53,16 +55,15 @@
%% Mech_State = term().
%% State of this process.
--record(sasl_state, {service, myname, realm,
- get_password, check_password, check_password_digest,
- mech_mod, mech_state}).
+-record(sasl_state, {service, myname,
+ mech_mod, mech_state, params}).
-export([behaviour_info/1]).
%% @hidden
behaviour_info(callbacks) ->
- [{mech_new, 4}, {mech_step, 2}];
+ [{mech_new, 1}, {mech_step, 2}];
behaviour_info(_Other) ->
undefined.
@@ -72,6 +73,7 @@ start() ->
ets:new(sasl_mechanism, [named_table,
public,
{keypos, #sasl_mechanism.mechanism}]),
+ cyrsasl_gssapi:start([]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
cyrsasl_anonymous:start([]),
@@ -158,13 +160,20 @@ listmech(Host) ->
%% CheckPassword = function()
server_new(Service, ServerFQDN, UserRealm, _SecFlags,
- GetPassword, CheckPassword, CheckPasswordDigest) ->
+ GetPassword, CheckPassword, CheckPasswordDigest, Socket) ->
+ Params = #sasl_params{
+ host = ServerFQDN,
+ realm = UserRealm,
+ get_password = GetPassword,
+ check_password = CheckPassword,
+ check_password_digest= CheckPasswordDigest,
+ socket = Socket
+ },
+
#sasl_state{service = Service,
myname = ServerFQDN,
- realm = UserRealm,
- get_password = GetPassword,
- check_password = CheckPassword,
- check_password_digest= CheckPasswordDigest}.
+ params = Params}.
+
%% @spec (State, Mech, ClientIn) -> Ok | Continue | Error
%% State = saslstate()
@@ -187,11 +196,8 @@ server_start(State, Mech, ClientIn) ->
true ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
- {ok, MechState} = Module:mech_new(
- State#sasl_state.myname,
- State#sasl_state.get_password,
- State#sasl_state.check_password,
- State#sasl_state.check_password_digest),
+ {ok, MechState} =
+ Module:mech_new(State#sasl_state.params),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);
diff --git a/src/cyrsasl.hrl b/src/cyrsasl.hrl
new file mode 100644
index 000000000..b4cc3e300
--- /dev/null
+++ b/src/cyrsasl.hrl
@@ -0,0 +1,15 @@
+%% @type saslparams() = {sasl_params, Host, Realm, GetPassword, CheckPassword, CheckPasswordDigest}
+%% Host = string()
+%% Realm = string()
+%% GetPassword = function()
+%% CheckPassword = function()
+%% CheckPasswordDigest = any().
+%% Parameters for SASL.
+
+-record(sasl_params, {
+ host,
+ realm,
+ get_password,
+ check_password,
+ check_password_digest,
+ socket}).
diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl
index 5d302c051..c20973256 100644
--- a/src/cyrsasl_anonymous.erl
+++ b/src/cyrsasl_anonymous.erl
@@ -27,7 +27,9 @@
-module(cyrsasl_anonymous).
--export([start/1, stop/0, mech_new/4, mech_step/2]).
+-export([start/1, stop/0, mech_new/1, mech_step/2]).
+
+-include("cyrsasl.hrl").
-behaviour(cyrsasl).
@@ -48,13 +50,7 @@ start(_Opts) ->
stop() ->
ok.
-%% @spec (Host, GetPassword, CheckPassword, CheckPasswordDigest) -> {ok, State}
-%% Host = string()
-%% GetPassword = function()
-%% CheckPassword = function()
-%% State = mechstate()
-
-mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
+mech_new(#sasl_params{host=Host}) ->
{ok, #state{server = Host}}.
%% @spec (State, ClientIn) -> Ok | Error
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
index eeef4c150..a4564951f 100644
--- a/src/cyrsasl_digest.erl
+++ b/src/cyrsasl_digest.erl
@@ -29,10 +29,11 @@
-export([start/1,
stop/0,
- mech_new/4,
+ mech_new/1,
mech_step/2]).
-include("ejabberd.hrl").
+-include("cyrsasl.hrl").
-behaviour(cyrsasl).
@@ -59,13 +60,8 @@ start(_Opts) ->
stop() ->
ok.
-%% @spec (Host, GetPassword, CheckPassword, CheckPasswordDigest) -> {ok, State}
-%% Host = string()
-%% GetPassword = function()
-%% CheckPassword = function()
-%% State = mechstate()
-
-mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
+mech_new(#sasl_params{host=Host, get_password=GetPassword,
+ check_password_digest=CheckPasswordDigest}) ->
{ok, #state{step = 1,
nonce = randoms:get_string(),
host = Host,
diff --git a/src/cyrsasl_gssapi.erl b/src/cyrsasl_gssapi.erl
new file mode 100644
index 000000000..5fdf071a5
--- /dev/null
+++ b/src/cyrsasl_gssapi.erl
@@ -0,0 +1,167 @@
+%%%----------------------------------------------------------------------
+%%% File : cyrsasl_gssapi.erl
+%%% Author : Mikael Magnusson <mikma@users.sourceforge.net>
+%%% Purpose : GSSAPI SASL mechanism
+%%% Created : 1 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
+%%% Id : $Id: $
+%%%----------------------------------------------------------------------
+%%%
+%%% Copyright (C) 2007-2009 Mikael Magnusson <mikma@users.sourceforge.net>
+%%%
+%%% Permission is hereby granted, free of charge, to any person
+%%% obtaining a copy of this software and associated documentation
+%%% files (the "Software"), to deal in the Software without
+%%% restriction, including without limitation the rights to use, copy,
+%%% modify, merge, publish, distribute, sublicense, and/or sell copies
+%%% of the Software, and to permit persons to whom the Software is
+%%% furnished to do so, subject to the following conditions:
+%%%
+%%% The above copyright notice and this permission notice shall be
+%%% included in all copies or substantial portions of the Software.
+%%%
+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+%%% SOFTWARE.
+%%%
+
+%%%
+%%% configuration options:
+%%% {sasl_realm, "<Kerberos realm>"}.
+%%%
+%%% environment variables:
+%%% KRB5_KTNAME
+%%%
+
+-module(cyrsasl_gssapi).
+-author('mikma@users.sourceforge.net').
+-vsn('$Revision: $ ').
+
+-export([start/1,
+ stop/0,
+ mech_new/1,
+ mech_step/2]).
+
+-include("ejabberd.hrl").
+-include("cyrsasl.hrl").
+
+-behaviour(cyrsasl).
+
+-define(SERVER, ?MODULE).
+-define(MSG, ?DEBUG).
+-define(SERVICE, "xmpp").
+
+-record(state, {sasl,
+ needsmore=true,
+ step=0,
+ host,
+ realm,
+ authid,
+ authzid,
+ authrealm,
+ error}).
+
+start(_Opts) ->
+ ChildSpec =
+ {?SERVER,
+ {esasl, start_link, [{local, ?SERVER}]},
+ transient,
+ 1000,
+ worker,
+ [esasl]},
+
+ case supervisor:start_child(ejabberd_sup, ChildSpec) of
+ {ok, _Pid} ->
+ cyrsasl:register_mechanism("GSSAPI", ?MODULE, false);
+ {error, Error} = E ->
+ ?ERROR_MSG("esasl failed: ~p", [Error]),
+ E
+ end.
+
+
+stop() ->
+ catch esasl:stop(?SERVER),
+ supervisor:terminate_child(ejabberd_sup, ?SERVER),
+ supervisor:delete_child(ejabberd_sup, ?SERVER).
+
+mech_new(#sasl_params{host=Host, realm=Realm, socket=Socket}) ->
+ case ejabberd_socket:gethostname(Socket) of
+ {ok, FQDN} ->
+ ?MSG("mech_new ~p ~p ~p~n", [Host, Realm, FQDN]),
+ case esasl:server_start(?SERVER, "GSSAPI", ?SERVICE, FQDN) of
+ {ok, Sasl} ->
+ {ok, #state{sasl=Sasl,host=Host,realm=Realm}};
+ {error, {gsasl_error, Error}} ->
+ {ok, Str} = esasl:str_error(?SERVER, Error),
+ ?MSG("esasl error: ~p", [Str]),
+ {ok, #state{needsmore=error,error="internal-server-error"}};
+ {error, Error} ->
+ ?MSG("esasl error: ~p", [Error]),
+ {ok, #state{needsmore=error,error="internal-server-error"}}
+ end;
+ {error, Error} ->
+ ?MSG("gethostname error: ~p", [Error]),
+ {ok, #state{needsmore=error,error="internal-server-error"}}
+ end.
+
+mech_step(State, ClientIn) when is_list(ClientIn) ->
+ catch do_step(State, ClientIn).
+
+do_step(#state{needsmore=error,error=Error}=State, _) ->
+ {error, Error};
+do_step(#state{needsmore=false}=State, _) ->
+ check_user(State);
+do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
+ ?MSG("mech_step~n", []),
+ case esasl:step(Sasl, list_to_binary(ClientIn)) of
+ {ok, RspAuth} ->
+ ?MSG("ok~n", []),
+ {ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name),
+ {ok, Authzid} = esasl:property_get(Sasl, authzid),
+ {Authid, [$@ | Auth_realm]} =
+ lists:splitwith(fun(E)->E =/= $@ end, Display_name),
+ State1 = State#state{authid=Authid,
+ authzid=Authzid,
+ authrealm=Auth_realm},
+ handle_step_ok(State1, binary_to_list(RspAuth));
+ {needsmore, RspAuth} ->
+ ?MSG("needsmore~n", []),
+ if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
+ {error, "not-authorized"};
+ true ->
+ {continue, binary_to_list(RspAuth),
+ State#state{step=Step+1}}
+ end;
+ {error, _} ->
+ {error, "not-authorized"}
+ end.
+
+handle_step_ok(State, []) ->
+ check_user(State);
+handle_step_ok(#state{step=Step}=State, RspAuth) ->
+ ?MSG("continue~n", []),
+ {continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
+
+check_user(#state{authid=Authid,authzid=Authzid,
+ authrealm=Auth_realm,host=Host,realm=Realm}) ->
+ if Realm =/= Auth_realm ->
+ ?MSG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
+ throw({error, "not-authorized"});
+ true ->
+ ok
+ end,
+
+ case ejabberd_auth:is_user_exists(Authid, Host) of
+ false ->
+ ?MSG("bad user ~p~n",[Authid]),
+ throw({error, "not-authorized"});
+ true ->
+ ok
+ end,
+
+ ?MSG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
+ {ok, [{username, Authid}, {authzid, Authzid}]}.
diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
index 0b1c873c3..690e99281 100644
--- a/src/cyrsasl_plain.erl
+++ b/src/cyrsasl_plain.erl
@@ -27,7 +27,9 @@
-module(cyrsasl_plain).
-author('alexey@process-one.net').
--export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
+-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]).
+
+-include("cyrsasl.hrl").
-behaviour(cyrsasl).
@@ -48,12 +50,6 @@ start(_Opts) ->
stop() ->
ok.
-%% @spec (Host, GetPassword, CheckPassword, CheckPasswordDigest) -> {ok, State}
-%% Host = string()
-%% GetPassword = function()
-%% CheckPassword = function()
-%% State = mechstate()
-
mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
{ok, #state{check_password = CheckPassword}}.
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 010fb177f..01d3c92c1 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -325,9 +325,16 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
send_header(StateData, Server, "1.0", DefaultLang),
case StateData#state.authenticated of
false ->
+ Realm =
+ case ejabberd_config:get_local_option({sasl_realm, Server}) of
+ undefined ->
+ "";
+ Realm0 ->
+ Realm0
+ end,
SASLState =
cyrsasl:server_new(
- "jabber", Server, "", [],
+ "jabber", Server, Realm, [],
fun(U) ->
ejabberd_auth:get_password_with_authmodule(
U, Server)
@@ -339,8 +346,9 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
fun(U, P, D, DG) ->
ejabberd_auth:check_password_with_authmodule(
U, Server, P, D, DG)
- end),
- SASL_Mechs = [exmpp_server_sasl:feature(
+ end,
+ StateData#state.socket),
+ Mechs = [exmpp_server_sasl:feature(
cyrsasl:listmech(Server))],
SockMod =
(StateData#state.sockmod):get_sockmod(
diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
index 8eeda452d..6de915b35 100644
--- a/src/ejabberd_socket.erl
+++ b/src/ejabberd_socket.erl
@@ -45,9 +45,11 @@
get_verify_result/1,
close/1,
change_controller/2,
+ gethostname/1,
sockname/1, peername/1]).
-include("ejabberd.hrl").
+-include_lib("kernel/include/inet.hrl").
-record(socket_state, {sockmod, socket, receiver}).
@@ -228,6 +230,23 @@ peername(#socket_state{sockmod = SockMod, socket = Socket}) ->
SockMod:peername(Socket)
end.
+gethostname(#socket_state{socket = Socket} = State) ->
+ ?DEBUG("gethostname ~p~n", [Socket]),
+
+ case sockname(State) of
+ {ok, {Addr, _Port}} ->
+ case inet:gethostbyaddr(Addr) of
+ {ok, HostEnt} when is_record(HostEnt, hostent) ->
+ ?DEBUG("gethostname result ~p~n",
+ [HostEnt#hostent.h_name]),
+ {ok, HostEnt#hostent.h_name};
+ {error, Reason} = E ->
+ E
+ end;
+ {error, Reason} = E ->
+ E
+ end.
+
%%====================================================================
%% Internal functions
%%====================================================================