There is patch for 2.1.12 version which can store password hashes instead plain passwords in mysql 'users' table.
This is optional. Additional option {password_type,hashed} should be set to enable this feature.
Also I added second option {auth_mechanisms, [digest]}. Which just starts only particular auth backend. Just I want to have exactly defined mechanisms. So for my purpose I use {auth_mechanisms, [digest,plain]}.
Note, mysql.sql schema is changed a bit. Thus to use patch on existent system ou need to alter 'users' table as it is in scheme.
Here is patch
Index: src/ejabberd_auth_odbc.erl
===================================================================
--- src/ejabberd_auth_odbc.erl	(.../vendor/2.1.12)	(revision 13)
+++ src/ejabberd_auth_odbc.erl	(.../trunk)	(revision 13)
@@ -70,11 +70,11 @@
 	    Username = ejabberd_odbc:escape(LUser),
 	    LServer = jlib:nameprep(Server),
 	    try odbc_queries:get_password(LServer, Username) of
-		{selected, ["password"], [{Password}]} ->
+		{selected, ["password","ha1"], [{Password,_Ha1}]} ->
 		    Password /= ""; %% Password is correct, and not empty
-		{selected, ["password"], [{_Password2}]} ->
+		{selected, ["password","ha1"], [{_Password2,_Ha1}]} ->
 		    false; %% Password is not correct
-		{selected, ["password"], []} ->
+		{selected, ["password","ha1"], []} ->
 		    false; %% Account does not exist
 		{error, _Error} ->
 		    false %% Typical error is that table doesn't exist
@@ -94,7 +94,7 @@
 	    LServer = jlib:nameprep(Server),
 	    try odbc_queries:get_password(LServer, Username) of
 		%% Account exists, check if password is valid
-		{selected, ["password"], [{Passwd}]} ->
+		{selected, ["password","ha1"], [{Passwd,_Ha1}]} ->
 		    DigRes = if
 				 Digest /= "" ->
 				     Digest == DigestGen(Passwd);
@@ -106,7 +106,7 @@
 		       true ->
 			    (Passwd == Password) and (Password /= "")
 		    end;
-		{selected, ["password"], []} ->
+		{selected, ["password","ha1"], []} ->
 		    false; %% Account does not exist
 		{error, _Error} ->
 		    false %% Typical error is that table doesn't exist
@@ -200,11 +200,19 @@
 	LUser ->
 	    Username = ejabberd_odbc:escape(LUser),
 	    LServer = jlib:nameprep(Server),
-	    case catch odbc_queries:get_password(LServer, Username) of
-		{selected, ["password"], [{Password}]} ->
-		    Password;
+	    {P,H} = case catch odbc_queries:get_password(LServer, Username) of
+		{selected, ["password","ha1"], [{Password,Ha1}]} ->
+		    {Password,Ha1};
 		_ ->
-		    false
+		    {null,null}
+	    end,
+	    case {P,H} of
+		{null,null} ->
+			false;
+		{_,null} ->
+			P;
+		_ ->
+			{P,H}
 	    end
     end.
@@ -215,11 +223,19 @@
 	LUser ->
 	    Username = ejabberd_odbc:escape(LUser),
 	    LServer = jlib:nameprep(Server),
-	    case catch odbc_queries:get_password(LServer, Username) of
-		{selected, ["password"], [{Password}]} ->
-		    Password;
+	    {P,H} = case catch odbc_queries:get_password(LServer, Username) of
+		{selected, ["password", "ha1"], [{Password,Ha1}]} ->
+		    {Password,Ha1};
 		_ ->
-		    ""
+		    {null,null}
+	    end,
+	    case {P,H} of
+		{null,null} ->
+			"";
+		{_,null} ->
+			P;
+		_ ->
+			{P,H}
 	    end
     end.
@@ -232,9 +248,9 @@
 	    Username = ejabberd_odbc:escape(LUser),
 	    LServer = jlib:nameprep(Server),
 	    try odbc_queries:get_password(LServer, Username) of
-		{selected, ["password"], [{_Password}]} ->
+		{selected, ["password","ha1"], [{_Password,_Ha1}]} ->
 		    true; %% Account exists
-		{selected, ["password"], []} ->
+		{selected, ["password","ha1"], []} ->
 		    false; %% Account does not exist
 		{error, Error} ->
 		    {error, Error} %% Typical error is that table doesn't exist
Index: src/cyrsasl_digest.erl
===================================================================
--- src/cyrsasl_digest.erl	(.../vendor/2.1.12)	(revision 13)
+++ src/cyrsasl_digest.erl	(.../trunk)	(revision 13)
@@ -78,15 +78,37 @@
 		    case (State#state.get_password)(UserName) of
 			{false, _} ->
 			    {error, "not-authorized", UserName};
+			{{Passwd, Ha1}, AuthModule} ->
+			    ?DEBUG("Funney-Hash ~p, ~p, ~p",[UserName,Passwd,Ha1]),
+ 			   case (State#state.check_password)(UserName, "",
+					xml:get_attr_s("response", KeyVals),
+					fun(PW) -> response(KeyVals, UserName, PW, Nonce, AuthzId,
+						"AUTHENTICATE",Ha1) end) of
+			    {true, _} ->
+
+			    	RspAuth = response(KeyVals,
+					       UserName, Passwd,
+					       Nonce, AuthzId, "", Ha1),
+			    	{continue,
+			     		"rspauth=" ++ RspAuth,
+			     		State#state{step = 5,
+						 auth_module = AuthModule,
+						 username = UserName,
+						 authzid = AuthzId}};
+			     false ->
+				    {error, "not-authorized", UserName};
+			     {false, _} ->
+				    {error, "not-authorized", UserName}
+			    end;
 			{Passwd, AuthModule} ->
 				case (State#state.check_password)(UserName, "",
 					xml:get_attr_s("response", KeyVals),
 					fun(PW) -> response(KeyVals, UserName, PW, Nonce, AuthzId,
-						"AUTHENTICATE") end) of
+						"AUTHENTICATE","") end) of
 				{true, _} ->
 				    RspAuth = response(KeyVals,
 						       UserName, Passwd,
-						       Nonce, AuthzId, ""),
+						       Nonce, AuthzId, "", ""),
 				    {continue,
 				     "rspauth=" ++ RspAuth,
 				     State#state{step = 5,
@@ -214,7 +236,7 @@
 	     digit_to_xchar(N div 16) | Res]).
-response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
+response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix, Ha1) ->
     Realm = xml:get_attr_s("realm", KeyVals),
     CNonce = xml:get_attr_s("cnonce", KeyVals),
     DigestURI = xml:get_attr_s("digest-uri", KeyVals),
@@ -222,13 +244,27 @@
     QOP = xml:get_attr_s("qop", KeyVals),
     A1 = case AuthzId of
 	     "" ->
-		 binary_to_list(
-		   crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
-		     ":" ++ Nonce ++ ":" ++ CNonce;
+		case Ha1 of
+			"" ->
+			 binary_to_list(
+			   	crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
+			     	":" ++ Nonce ++ ":" ++ CNonce;
+			_ ->
+			 binary_to_list(
+			   	hex:hexstr_to_bin(Ha1)) ++
+			     	":" ++ Nonce ++ ":" ++ CNonce
+		end;
 	     _ ->
-		 binary_to_list(
-		   crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
-		     ":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
+		case Ha1 of
+			"" ->
+		 		binary_to_list(
+				   crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
+				     ":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId;
+			_ ->
+		 		binary_to_list(
+			   		hex:hexstr_to_bin(Ha1)) ++
+				     ":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
+		end
 	 end,
     A2 = case QOP of
 	     "auth" ->
Index: src/cyrsasl.erl
===================================================================
--- src/cyrsasl.erl	(.../vendor/2.1.12)	(revision 13)
+++ src/cyrsasl.erl	(.../trunk)	(revision 13)
@@ -52,12 +52,50 @@
     ets:new(sasl_mechanism, [named_table,
 			     public,
 			     {keypos, #sasl_mechanism.mechanism}]),
+    
+    Mechs = ejabberd_config:get_local_option(auth_mechanisms),
+    case Mechs of
+        undefined ->
+            start_all_mechs();
+        _ ->
+            case is_atom(Mechs) of
+                true ->
+                    start_all_mechs();
+                false ->
+                    case lists:flatlength(Mechs) of 
+                        0 ->
+                            start_all_mechs();
+                        _ ->
+                            start_mechs(Mechs)
+                    end
+            end
+    end,
+    ok.
+
+start_all_mechs() ->
     cyrsasl_plain:start([]),
     cyrsasl_digest:start([]),
     cyrsasl_scram:start([]),
-    cyrsasl_anonymous:start([]),
-    ok.
+    cyrsasl_anonymous:start([]).
+start_mechs([M | Mechs]) ->
+    ?DEBUG("Starting: ~p",[M]),
+    case M of
+        digest ->
+            cyrsasl_digest:start([]);
+        plain ->
+            cyrsasl_plain:start([]);
+        scram ->
+            cyrsasl_scram:start([]);
+        anon ->
+            cyrsasl_anonymous:start([])
+    end,
+    start_mechs(Mechs);
+
+start_mechs([]) ->
+	[].
+
+
 register_mechanism(Mechanism, Module, PasswordType) ->
     ets:insert(sasl_mechanism,
 	       #sasl_mechanism{mechanism = Mechanism,
Index: src/odbc/mysql.sql
===================================================================
--- src/odbc/mysql.sql	(.../vendor/2.1.12)	(revision 13)
+++ src/odbc/mysql.sql	(.../trunk)	(revision 13)
@@ -23,6 +23,7 @@
 CREATE TABLE users (
     username varchar(250) PRIMARY KEY,
     password text NOT NULL,
+    ha1 text,
     created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
 ) CHARACTER SET utf8;
Index: src/odbc/odbc_queries.erl
===================================================================
--- src/odbc/odbc_queries.erl	(.../vendor/2.1.12)	(revision 13)
+++ src/odbc/odbc_queries.erl	(.../trunk)	(revision 13)
@@ -167,23 +167,46 @@
 get_password(LServer, Username) ->
     ejabberd_odbc:sql_query(
       LServer,
-      ["select password from users "
+      ["select password, ha1 from users "
        "where username='", Username, "';"]).
 set_password_t(LServer, Username, Pass) ->
-    ejabberd_odbc:sql_transaction(
-      LServer,
-      fun() ->
-	      update_t("users", ["username", "password"],
-		       [Username, Pass],
-		       ["username='", Username ,"'"])
-      end).
+    PS = ejabberd_config:get_local_option(password_type),
+    case PS of
+        hashed ->
+	        Ha1 = hex:bin_to_hexstr(crypto:md5(Username ++ ":" ++ LServer ++ ":" ++ Pass)),
+	        ejabberd_odbc:sql_transaction(
+	        LServer,
+	        fun() ->
+		        update_t("users", ["username", "ha1"],
+			       [Username, Ha1],
+			       ["username='", Username ,"'"])
+	        end);
+        _ ->
+	    ejabberd_odbc:sql_transaction(
+	        LServer,
+	        fun() ->
+		        update_t("users", ["username", "password"],
+			       [Username, Pass],
+			       ["username='", Username ,"'"])
+	        end)
+    end.
 add_user(LServer, Username, Pass) ->
-    ejabberd_odbc:sql_query(
-      LServer,
-      ["insert into users(username, password) "
-       "values ('", Username, "', '", Pass, "');"]).
+    PS = ejabberd_config:get_local_option(password_type),
+    case PS of
+        hashed ->
+            Ha1 = hex:bin_to_hexstr(crypto:md5(Username ++ ":" ++ LServer ++ ":" ++ Pass)),
+                ejabberd_odbc:sql_query(
+    	            LServer,
+	                ["insert into users(username, ha1) "
+	                "values ('", Username, "', '", Ha1, "');"]);
+        _ ->
+            ejabberd_odbc:sql_query(
+    	        LServer,
+	            ["insert into users(username, password) "
+	            "values ('", Username, "', '", Pass, "');"])
+    end.
 del_user(LServer, Username) ->
     ejabberd_odbc:sql_query(
@@ -191,12 +214,24 @@
       ["delete from users where username='", Username ,"';"]).
 del_user_return_password(_LServer, Username, Pass) ->
-    P = ejabberd_odbc:sql_query_t(
-	  ["select password from users where username='",
-	   Username, "';"]),
-    ejabberd_odbc:sql_query_t(["delete from users "
-			       "where username='", Username,
-			       "' and password='", Pass, "';"]),
+    PS = ejabberd_config:get_local_option(password_type),
+    P = case PS of
+        hashed ->
+            Ha1 = hex:bin_to_hexstr(crypto:md5(Username ++ ":" ++ _LServer ++ ":" ++ Pass)),
+            ejabberd_odbc:sql_query_t(
+	            ["select ha1 from users where username='",
+	            Username, "';"]),
+            ejabberd_odbc:sql_query_t(["delete from users "
+		        "where username='", Username,
+			    "' and ha1='", Ha1, "';"]);
+        _ ->
+            ejabberd_odbc:sql_query_t(
+	            ["select password from users where username='",
+	            Username, "';"]),
+            ejabberd_odbc:sql_query_t(["delete from users "
+		        "where username='", Username,
+			    "' and password='", Pass, "';"])
+        end,
     P.
 list_users(LServer) ->
Index: src/ejabberd.cfg.example
===================================================================
--- src/ejabberd.cfg.example	(.../vendor/2.1.12)	(revision 13)
+++ src/ejabberd.cfg.example	(.../trunk)	(revision 13)
@@ -90,6 +90,9 @@
 %%
 {hosts, ["localhost"]}.
+%%{auth_mechanisms,[digest,plain]}.
+%%{password_type,hashed}.
+
 %%
 %% route_subdomains: Delegate subdomains to other XMPP servers.
 %% For example, if this ejabberd serves example.org and you want
Index: src/ejabberd_config.erl
===================================================================
--- src/ejabberd_config.erl	(.../vendor/2.1.12)	(revision 13)
+++ src/ejabberd_config.erl	(.../trunk)	(revision 13)
@@ -443,6 +443,10 @@
 	    State;
 	{max_fsm_queue, N} ->
 	    add_option(max_fsm_queue, N, State);
+	{password_type, PasswordType} ->
+	    add_option(password_type, PasswordType, State);
+	{auth_mechanisms, AuthMechs} ->
+	    add_option(auth_mechanisms, AuthMechs, State);
 	{_Opt, _Val} ->
 	    lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
 			State, State#state.hosts)
Index: src/hex.erl
===================================================================
--- src/hex.erl	(.../vendor/2.1.12)	(revision 0)
+++ src/hex.erl	(.../trunk)	(revision 13)
@@ -0,0 +1,33 @@
+-module(hex).
+-export([bin_to_hexstr/1,hexstr_to_bin/1]).
+
+hex(N) when N < 10 ->
+    $0+N;
+hex(N) when N >= 10, N < 16 ->
+    $a+(N-10).
+
+int(C) when $0 =< C, C =< $9 ->
+    C - $0;
+int(C) when $A =< C, C =< $F ->
+    C - $A + 10;
+int(C) when $a =< C, C =< $f ->
+    C - $a + 10.
+    
+to_hex(N) when N < 256 ->
+    [hex(N div 16), hex(N rem 16)].
+ 
+list_to_hexstr([]) -> 
+    [];
+list_to_hexstr([H|T]) ->
+    to_hex(H) ++ list_to_hexstr(T).
+
+bin_to_hexstr(Bin) ->
+    list_to_hexstr(binary_to_list(Bin)).
+
+hexstr_to_bin(S) ->
+    list_to_binary(hexstr_to_list(S)).
+
+hexstr_to_list([X,Y|T]) ->
+    [int(X)*16 + int(Y) | hexstr_to_list(T)];
+hexstr_to_list([]) ->
+    [].