/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.db; //~--- non-JDK imports -------------------------------------------------------- import tigase.util.Algorithms; import tigase.util.Base64; import tigase.util.TigaseStringprepException; import tigase.xmpp.BareJID; import static tigase.db.AuthRepository.*; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; //~--- classes ---------------------------------------------------------------- /** * Describe class AuthRepositoryImpl here. * * * Created: Sat Nov 11 21:46:50 2006 * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class AuthRepositoryImpl implements AuthRepository { protected static final Logger log = Logger.getLogger("tigase.db.UserAuthRepositoryImpl"); protected static final String PASSWORD_KEY = "password"; private static final String[] non_sasl_mechs = { "password", "digest" }; private static final String[] sasl_mechs = { "PLAIN", "DIGEST-MD5", "CRAM-MD5" }; // ~--- fields --------------------------------------------------------------- private UserRepository repo = null; // ~--- constructors --------------------------------------------------------- /** * Creates a new <code>AuthRepositoryImpl</code> instance. * * * @param repo */ public AuthRepositoryImpl(UserRepository repo) { this.repo = repo; } // ~--- methods -------------------------------------------------------------- /** * Describe <code>addUser</code> method here. * * @param user * a <code>String</code> value * @param password * a <code>String</code> value * @exception UserExistsException * if an error occurs * @exception TigaseDBException * if an error occurs */ @Override public void addUser(BareJID user, final String password) throws UserExistsException, TigaseDBException { repo.addUser(user); log.info("Repo user added: " + user); updatePassword(user, password); log.info("Password updated: " + user + ":" + password); } /** * Describe <code>digestAuth</code> method here. * * @param user * a <code>String</code> value * @param digest * a <code>String</code> value * @param id * a <code>String</code> value * @param alg * a <code>String</code> value * @return a <code>boolean</code> value * @exception UserNotFoundException * if an error occurs * @exception TigaseDBException * if an error occurs * @exception AuthorizationException * if an error occurs */ @Override @Deprecated public boolean digestAuth(BareJID user, final String digest, final String id, final String alg) throws UserNotFoundException, TigaseDBException, AuthorizationException { final String db_password = getPassword(user); try { final String digest_db_pass = Algorithms.hexDigest(id, db_password, alg); if (log.isLoggable(Level.FINEST)) { log.finest("Comparing passwords, given: " + digest + ", db: " + digest_db_pass); } return digest.equals(digest_db_pass); } catch (NoSuchAlgorithmException e) { throw new AuthorizationException("No such algorithm.", e); } // end of try-catch } // ~--- get methods ---------------------------------------------------------- /** * Method description * * * @return */ @Override public String getResourceUri() { return repo.getResourceUri(); } /** * Method description * * * @return */ @Override public long getUsersCount() { return repo.getUsersCount(); } /** * Method description * * * @param domain * * @return */ @Override public long getUsersCount(String domain) { return repo.getUsersCount(domain); } // ~--- methods -------------------------------------------------------------- /** * Describe <code>initRepository</code> method here. * * @param string * a <code>String</code> value * @param params * @exception DBInitException * if an error occurs */ @Override public void initRepository(final String string, Map<String, String> params) throws DBInitException { } /** * Method description * * * @param user */ @Override public void logout(BareJID user) { } /** * Describe <code>otherAuth</code> method here. * * @param props * a <code>Map</code> value * @return a <code>boolean</code> value * @exception UserNotFoundException * if an error occurs * @exception TigaseDBException * if an error occurs * @exception AuthorizationException * if an error occurs */ @Override public boolean otherAuth(final Map<String, Object> props) throws UserNotFoundException, TigaseDBException, AuthorizationException { String proto = (String) props.get(PROTOCOL_KEY); // TODO: this equals should be most likely replaced with == here. // The property value is always set using the constant.... if (proto.equals(PROTOCOL_VAL_SASL)) { return saslAuth(props); } // end of if (proto.equals(PROTOCOL_VAL_SASL)) if (proto.equals(PROTOCOL_VAL_NONSASL)) { String password = (String) props.get(PASSWORD_KEY); BareJID user_id = (BareJID) props.get(USER_ID_KEY); if (password != null) { return plainAuth(user_id, password); } String digest = (String) props.get(DIGEST_KEY); if (digest != null) { String digest_id = (String) props.get(DIGEST_ID_KEY); return digestAuth(user_id, digest, digest_id, "SHA"); } } // end of if (proto.equals(PROTOCOL_VAL_SASL)) throw new AuthorizationException("Protocol is not supported."); } /** * Describe <code>plainAuth</code> method here. * * @param user * a <code>String</code> value * @param password * a <code>String</code> value * @return a <code>boolean</code> value * @exception UserNotFoundException * if an error occurs * @exception TigaseDBException * if an error occurs */ @Override @Deprecated public boolean plainAuth(BareJID user, final String password) throws UserNotFoundException, TigaseDBException { String db_password = getPassword(user); return (password != null) && (db_password != null) && db_password.equals(password); } // Implementation of tigase.db.AuthRepository /** * Describe <code>queryAuth</code> method here. * * @param authProps * a <code>Map</code> value */ @Override public void queryAuth(final Map<String, Object> authProps) { String protocol = (String) authProps.get(PROTOCOL_KEY); if (protocol.equals(PROTOCOL_VAL_NONSASL)) { authProps.put(RESULT_KEY, non_sasl_mechs); } // end of if (protocol.equals(PROTOCOL_VAL_NONSASL)) if (protocol.equals(PROTOCOL_VAL_SASL)) { authProps.put(RESULT_KEY, sasl_mechs); } // end of if (protocol.equals(PROTOCOL_VAL_NONSASL)) } /** * Describe <code>removeUser</code> method here. * * @param user * a <code>String</code> value * @exception UserNotFoundException * if an error occurs * @exception TigaseDBException * if an error occurs */ @Override public void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException { repo.removeUser(user); } /** * Describe <code>updatePassword</code> method here. * * @param user * a <code>String</code> value * @param password * a <code>String</code> value * @exception TigaseDBException * if an error occurs */ @Override public void updatePassword(BareJID user, final String password) throws TigaseDBException { repo.setData(user, PASSWORD_KEY, password); } // ~--- get methods ---------------------------------------------------------- private String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException { return repo.getData(user, PASSWORD_KEY); } // ~--- methods -------------------------------------------------------------- private boolean saslAuth(final Map<String, Object> props) throws AuthorizationException { try { SaslServer ss = (SaslServer) props.get("SaslServer"); if (ss == null) { Map<String, String> sasl_props = new TreeMap<String, String>(); sasl_props.put(Sasl.QOP, "auth"); ss = Sasl.createSaslServer((String) props.get(MACHANISM_KEY), "xmpp", (String) props.get(SERVER_NAME_KEY), sasl_props, new SaslCallbackHandler( props)); props.put("SaslServer", ss); } // end of if (ss == null) String data_str = (String) props.get(DATA_KEY); byte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]); if (log.isLoggable(Level.FINEST)) { log.finest("response: " + new String(in_data)); } byte[] challenge = ss.evaluateResponse(in_data); if (log.isLoggable(Level.FINEST)) { log.finest("challenge: " + ((challenge != null) ? new String(challenge) : "null")); } String challenge_str = (((challenge != null) && (challenge.length > 0)) ? Base64.encode(challenge) : null); props.put(RESULT_KEY, challenge_str); if (ss.isComplete()) { return true; } else { return false; } // end of if (ss.isComplete()) else } catch (SaslException e) { throw new AuthorizationException("Sasl exception.", e); } // end of try-catch } // ~--- inner classes -------------------------------------------------------- private class SaslCallbackHandler implements CallbackHandler { private Map<String, Object> options = null; // ~--- constructors ------------------------------------------------------- private SaslCallbackHandler(final Map<String, Object> options) { this.options = options; } // ~--- methods ------------------------------------------------------------ // Implementation of javax.security.auth.callback.CallbackHandler /** * Describe <code>handle</code> method here. * * @param callbacks * a <code>Callback[]</code> value * @exception IOException * if an error occurs * @exception UnsupportedCallbackException * if an error occurs */ @Override public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { BareJID jid = null; for (int i = 0; i < callbacks.length; i++) { if (log.isLoggable(Level.FINEST)) { log.finest("Callback: " + callbacks[i].getClass().getSimpleName()); } if (callbacks[i] instanceof RealmCallback) { RealmCallback rc = (RealmCallback) callbacks[i]; String realm = (String) options.get(REALM_KEY); if (realm != null) { rc.setText(realm); } // end of if (realm == null) if (log.isLoggable(Level.FINEST)) { log.finest("RealmCallback: " + realm); } } else { if (callbacks[i] instanceof NameCallback) { NameCallback nc = (NameCallback) callbacks[i]; String user_name = nc.getName(); if (user_name == null) { user_name = nc.getDefaultName(); } // end of if (name == null) jid = BareJID.bareJIDInstanceNS(user_name, (String) options.get(REALM_KEY)); options.put(USER_ID_KEY, jid); if (log.isLoggable(Level.FINEST)) { log.finest("NameCallback: " + user_name); } } else { if (callbacks[i] instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) callbacks[i]; try { String passwd = getPassword(jid); pc.setPassword(passwd.toCharArray()); if (log.isLoggable(Level.FINEST)) { log.finest("PasswordCallback: " + passwd); } } catch (Exception e) { throw new IOException("Password retrieving problem.", e); } // end of try-catch } else { if (callbacks[i] instanceof AuthorizeCallback) { AuthorizeCallback authCallback = ((AuthorizeCallback) callbacks[i]); String authenId = authCallback.getAuthenticationID(); String authorId = authCallback.getAuthorizationID(); if (log.isLoggable(Level.FINEST)) { log.finest("AuthorizeCallback: authenId: " + authenId); log.finest("AuthorizeCallback: authorId: " + authorId); } // if (authenId.equals(authorId)) { authCallback.setAuthorized(true); // } // end of if (authenId.equals(authorId)) } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); } } } } } } } } // AuthRepositoryImpl // ~ Formatted in Sun Code Convention // ~ Formatted by Jindent --- http://www.jindent.com