Index: src/java/org/jivesoftware/wildfire/handler/IQAuthHandler.java =================================================================== --- src/java/org/jivesoftware/wildfire/handler/IQAuthHandler.java (revision 1) +++ src/java/org/jivesoftware/wildfire/handler/IQAuthHandler.java (revision 2) @@ -29,9 +29,11 @@ import org.xmpp.packet.JID; import org.xmpp.packet.PacketError; import org.xmpp.packet.StreamError; +import org.jivesoftware.wildfire.net.SASLAuthentication; import java.util.ArrayList; import java.util.List; +import java.util.Collection; /** * Implements the TYPE_IQ jabber:iq:auth protocol (plain only). Clients @@ -79,7 +81,9 @@ probeResponse.addElement("digest"); } probeResponse.addElement("resource"); + anonymousAllowed = "true".equals(JiveGlobals.getProperty("xmpp.auth.anonymous")); + anonymousAllowed = anonymousAllowed && SASLAuthentication.getMechList().contains("ANONYMOUS"); } public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException { @@ -165,6 +169,11 @@ ClientSession session, String digest) throws UnauthorizedException, UserNotFoundException { + if(! SASLAuthentication.getMechList().contains("PLAIN")) { + //plain authentication no allowed + throw new UnauthorizedException(); + } + String resource = iq.elementTextTrim("resource"); if (resource != null) { try { @@ -256,7 +265,7 @@ private IQ anonymousLogin(ClientSession session, IQ packet) { IQ response = IQ.createResultIQ(packet); - if (anonymousAllowed) { + if (isAllowAnonymous()) { session.setAnonymousAuth(); response.setTo(session.getAddress()); Element auth = response.setChildElement("query", "jabber:iq:auth"); @@ -270,7 +279,7 @@ } public boolean isAllowAnonymous() { - return anonymousAllowed; + return anonymousAllowed && SASLAuthentication.getMechList().contains("ANONYMOUS"); } public void setAllowAnonymous(boolean isAnonymous) throws UnauthorizedException { @@ -288,4 +297,4 @@ public IQHandlerInfo getInfo() { return info; } -} \ No newline at end of file +} Index: src/java/org/jivesoftware/wildfire/net/XMPPCallbackHandler.java =================================================================== --- src/java/org/jivesoftware/wildfire/net/XMPPCallbackHandler.java (revision 1) +++ src/java/org/jivesoftware/wildfire/net/XMPPCallbackHandler.java (revision 2) @@ -12,6 +12,8 @@ import org.jivesoftware.wildfire.auth.AuthFactory; import org.jivesoftware.wildfire.user.UserNotFoundException; +import org.jivesoftware.wildfire.sasl.AuthorizationManager; +import org.jivesoftware.util.Log; import javax.security.auth.callback.*; import javax.security.sasl.AuthorizeCallback; @@ -20,8 +22,7 @@ /** * Callback handler that may be used when doing SASL authentication. A CallbackHandler - * may be required depending on the SASL mechanism being used. Currently DIGEST-MD5 and - * CRAM-MD5 are the only mechanisms that will require a callback handler.

+ * may be required depending on the SASL mechanism being used.

* * Mechanisms that use a digest don't include a password so the server needs to use the * stored password of the user to compare it (somehow) with the specified digest. This @@ -74,13 +75,15 @@ } else if (callbacks[i] instanceof AuthorizeCallback) { AuthorizeCallback authCallback = ((AuthorizeCallback) callbacks[i]); - String authenId = authCallback.getAuthenticationID(); - String authorId = authCallback.getAuthorizationID(); - if (authenId.equals(authorId)) { + String authenId = authCallback.getAuthenticationID(); // Principal that authenticated + String authorId = authCallback.getAuthorizationID(); // Username requested (not full JID) + if(AuthorizationManager.authorize(authorId,authenId)) { authCallback.setAuthorized(true); authCallback.setAuthorizedID(authorId); + Log.debug(authenId + " authorized to " + authorId); + } else { + Log.debug(authenId + " not authorized to " + authorId); } - //Log.info("AuthorizeCallback: authorId: " + authorId); } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); Index: src/java/org/jivesoftware/wildfire/net/SASLAuthentication.java =================================================================== --- src/java/org/jivesoftware/wildfire/net/SASLAuthentication.java (revision 1) +++ src/java/org/jivesoftware/wildfire/net/SASLAuthentication.java (revision 2) @@ -37,7 +37,11 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; +import java.util.Collection; +import java.util.ArrayList; +import org.ietf.jgss.*; + /** * SASLAuthentication is responsible for returning the available SASL mechanisms to use and for * actually performing the SASL authentication.

@@ -60,6 +64,8 @@ private static final String SASL_NAMESPACE = "xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\""; private static Map typeMap = new TreeMap(); + + private static Collection mechs = null; public enum ElementType { @@ -97,6 +103,12 @@ this.session = session; this.connection = (SocketConnection) session.getConnection(); this.reader = reader; + + + + + + } /** @@ -110,19 +122,27 @@ StringBuilder sb = new StringBuilder(195); sb.append(""); if (session.getConnection().isSecure() && session instanceof IncomingServerSession) { + // Server connections dont follow the same rules as clients sb.append("EXTERNAL"); } else { - // Check if the user provider in use supports passwords retrieval. Accessing to the users - // passwords will be required by the CallbackHandler - if (UserManager.getUserProvider().supportsPasswordRetrieval()) { - sb.append("CRAM-MD5"); - sb.append("DIGEST-MD5"); + for (String mech : getMechList()) { + if (mech.equals("CRAM-MD5") || mech.equals("DIGEST-MD5")) { + // Check if the user provider in use supports passwords retrieval. Accessing to the users + // passwords will be required by the CallbackHandler + if (! UserManager.getUserProvider().supportsPasswordRetrieval()) { + continue; + } + } else if (mech.equals("ANONYMOUS")) { + // Check anonymous is supported + if (! XMPPServer.getInstance().getIQAuthHandler().isAllowAnonymous()) { + continue; + } + } + sb.append(""); + sb.append(mech); + sb.append(""); } - sb.append("PLAIN"); - if (XMPPServer.getInstance().getIQAuthHandler().isAllowAnonymous()) { - sb.append("ANONYMOUS"); - } } sb.append(""); return sb.toString(); @@ -133,19 +153,28 @@ throws IOException, DocumentException, XmlPullParserException { boolean isComplete = false; boolean success = false; - + while (!isComplete) { if (doc.getNamespace().asXML().equals(SASL_NAMESPACE)) { ElementType type = ElementType.valueof(doc.getName()); switch (type) { case AUTH: String mechanism = doc.attributeValue("mechanism"); + Log.debug("SASLAuthentication.doHandshake() AUTH entered: "+mechanism); if (mechanism.equalsIgnoreCase("PLAIN")) { - success = doPlainAuthentication(doc); + if(getMechList().contains("PLAIN")) { + success = doPlainAuthentication(doc); + } else { + success = false; + } isComplete = true; } else if (mechanism.equalsIgnoreCase("ANONYMOUS")) { - success = doAnonymousAuthentication(); + if(getMechList().contains("ANONYMOUS")) { + success = doAnonymousAuthentication(); + } else { + success = false; + } isComplete = true; } else if (mechanism.equalsIgnoreCase("EXTERNAL")) { @@ -155,31 +184,41 @@ else { // The selected SASL mechanism requires the server to send a challenge // to the client - try { - Map props = new TreeMap(); - props.put(Sasl.QOP, "auth"); - SaslServer ss = Sasl.createSaslServer(mechanism, "xmpp", - session.getServerName(), props, - new XMPPCallbackHandler()); - // evaluateResponse doesn't like null parameter - byte[] token = new byte[0]; - if (doc.isTextOnly()) { - // If auth request includes a value then validate it - token = StringUtils.decodeBase64(doc.getText()); - if (token == null) { - token = new byte[0]; + if(getMechList().contains(mechanism)) { + try { + + Map props = new TreeMap(); + props.put(Sasl.QOP, "auth"); + if(mechanism.equals("GSSAPI")) { + props.put(Sasl.SERVER_AUTH,"TRUE"); } + SaslServer ss = Sasl.createSaslServer(mechanism, "xmpp", + session.getServerName(), props, + new XMPPCallbackHandler()); + // evaluateResponse doesn't like null parameter + byte[] token = new byte[0]; + if (doc.isTextOnly()) { + // If auth request includes a value then validate it + token = StringUtils.decodeBase64(doc.getTextTrim()); + if (token == null) { + token = new byte[0]; + } + } + byte[] challenge = ss.evaluateResponse(token); + // Send the challenge + sendChallenge(challenge); + + session.setSessionData("SaslServer", ss); } - byte[] challenge = ss.evaluateResponse(token); - // Send the challenge - sendChallenge(challenge); - - session.setSessionData("SaslServer", ss); - } - catch (SaslException e) { + catch (SaslException e) { + isComplete = true; + Log.warn("SaslException", e); + authenticationFailed(); + } + } else { + Log.warn("Client wants to do a MECH we dont support: '"+mechanism+"'"); isComplete = true; - Log.warn("SaslException", e); - authenticationFailed(); + success = false; } } break; @@ -337,6 +376,9 @@ private void sendChallenge(byte[] challenge) { StringBuilder reply = new StringBuilder(250); + if(challenge == null) { + challenge = new byte[0]; + } String challenge_b64 = StringUtils.encodeBase64(challenge).trim(); if ("".equals(challenge_b64)) { challenge_b64 = "="; // Must be padded if null @@ -383,4 +425,37 @@ connection.close(); } } + + public static Collection getMechList() { + if (mechs == null) { + mechs = new ArrayList(); + StringTokenizer st = new StringTokenizer(JiveGlobals.getXMLProperty("sasl.mechs"), " ,\t\n\r\f"); + while (st.hasMoreTokens()) { + String mech = st.nextToken().toUpperCase(); + // Check that the mech is a supported mechansim. Maybe we shouldnt check this and allow any? + if(mech.equals("ANONYMOUS") || + mech.equals("PLAIN") || + mech.equals("DIGEST-MD5") || + mech.equals("CRAM-MD5") || + mech.equals("GSSAPI") ) { + Log.debug("SASLAuthentication: Added "+mech+" to mech list"); + mechs.add(mech); + } + } + + if(getMechList().contains("GSSAPI")) { + if(JiveGlobals.getXMLProperty("sasl.gssapi.config") != null) { + System.setProperty("java.security.krb5.debug", JiveGlobals.getXMLProperty("sasl.gssapi.debug","false")); + System.setProperty("java.security.auth.login.config",JiveGlobals.getXMLProperty("sasl.gssapi.config")); + System.setProperty("javax.security.auth.useSubjectCredsOnly",JiveGlobals.getXMLProperty("sasl.gssapi.useSubjectCredsOnly","false")); + } else { + //Not configured, remove the option. + Log.debug("SASLAuthentication: Removed GSSAPI from mech list"); + mechs.remove("GSSAPI"); + } + } + + } + return mechs; + } } Index: src/java/org/jivesoftware/wildfire/sasl/LazyAuthorizationPolicy.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/LazyAuthorizationPolicy.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/LazyAuthorizationPolicy.java (revision 2) @@ -0,0 +1,42 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-20 10:46:24 -0500 (Thu, 20 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; +import org.jivesoftware.util.JiveGlobals; + +/** + * This policy will authorize any principal who's username matches exactly + * the username of the JID. This means when cross realm authentication is + * allowed, user@REALM_A.COM and user@REALM_B.COM could both authorize as + * user@servername, so there is some risk here. But if usernames across the + * + * @author Jay Kline + */ +public class LazyAuthorizationPolicy implements AuthorizationPolicyProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException { + if(principal.startsWith(username+"@")) { + return; + } else { + throw new UnauthorizedException(); + } + } +} \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationProvider.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationProvider.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationProvider.java (revision 2) @@ -0,0 +1,194 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-07 09:28:54 -0500 (Fri, 07 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.database.DbConnectionManager; +import org.jivesoftware.wildfire.auth.UnauthorizedException; +import org.jivesoftware.util.Log; +import org.jivesoftware.util.LocaleUtils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Connection; +import java.util.Collection; +import java.util.ArrayList; + + +/** + * Provider for authorization using the default storage database. Checks + * if the authenticated principal is in the user's list of authorized + * principals. + * + * @author Jay Kline + */ +public class DefaultAuthorizationProvider implements AuthorizationProvider { + + private static final String MATCH_AUTHORIZED = "SELECT username FROM jiveAuthorized WHERE username=? AND authorized=?"; + private static final String GET_AUTHORIZED = "SELECT authorized FROM jiveAuthorized WHERE username=?"; + private static final String INSERT_AUTHORIZED = "INSERT into jiveAuthorized (username,authorized) VALUES (?,?)"; + private static final String DELETE_AUTHORIZED = "DELETE FROM jiveAuthorized WHERE username=? AND authorized=?"; + private static final String DELETE_USER = "DELETE FROM jiveAuthorized WHERE username=?"; + + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException { + Connection con = null; + PreparedStatement pstmt = null; + try { + con = DbConnectionManager.getConnection(); + pstmt = con.prepareStatement(MATCH_AUTHORIZED); + pstmt.setString(1, username); + pstmt.setString(2, principal); + ResultSet rs = pstmt.executeQuery(); + if (!rs.next()) { + throw new UnauthorizedException(); + } + return; + } + catch (Exception e) { + throw new UnauthorizedException(e); + } + finally { + try { if (pstmt != null) { pstmt.close(); } } + catch (Exception e) { Log.error(e); } + try { if (con != null) { con.close(); } } + catch (Exception e) { Log.error(e); } + } + } + + /** + * Returns a String Collection of principals that are authorized to use + * the named user. + * + * @param username The username. + * @return A String Collection of principals that are authorized. + */ + public Collection getAuthorized(String username) { + Connection con = null; + PreparedStatement pstmt = null; + Collection authorized = new ArrayList(); + try { + con = DbConnectionManager.getConnection(); + pstmt = con.prepareStatement(GET_AUTHORIZED); + pstmt.setString(1,username); + ResultSet rs = pstmt.executeQuery(); + + while(rs.next()) { + authorized.add(rs.getString("authorized")); + } + return authorized; + } catch (Exception e) { + return new ArrayList(); + } + finally { + try { if (pstmt != null) { pstmt.close(); } } + catch (Exception e) { Log.error(e); } + try { if (con != null) { con.close(); } } + catch (Exception e) { Log.error(e); } + } + } + + + + /** + * Returns true. + * + * @return true + */ + public boolean isWritable() { + return true; + } + + /** + * Add a single authorized principal to use the named user. + * + * @param username The username. + * @param principal The principal authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, String principal) throws UnsupportedOperationException { + try { + authorize(username,principal); + // Already exists since no exception + return; + } + catch (UnauthorizedException unae) { + Connection con = null; + PreparedStatement pstmt = null; + try { + con = DbConnectionManager.getConnection(); + pstmt = con.prepareStatement(INSERT_AUTHORIZED); + pstmt.setString(1, username); + pstmt.setString(2, principal); + pstmt.execute(); + } + catch (Exception e) { + Log.error(LocaleUtils.getLocalizedString("admin.error"), e); + } + finally { + try { if (pstmt != null) { pstmt.close(); } } + catch (Exception e) { Log.error(e); } + try { if (con != null) { con.close(); } } + catch (Exception e) { Log.error(e); } + } + } + } + + /** + * Add a Collection of users authorized to use the named user. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, Collection principals) throws UnsupportedOperationException { + for( String principal : principals ) { + addAuthorized(username,principal); + } + } + + /** + * Set the users authorized to use the named user. All existing principals listed + * will be removed. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void setAuthorized(String username, Collection principals) throws UnsupportedOperationException { + Connection con = null; + PreparedStatement pstmt = null; + try { + con = DbConnectionManager.getConnection(); + pstmt = con.prepareStatement(DELETE_USER); + pstmt.setString(1, username); + pstmt.execute(); + addAuthorized(username,principals); + } + catch (Exception e) { + Log.error(LocaleUtils.getLocalizedString("admin.error"), e); + } + finally { + try { if (pstmt != null) { pstmt.close(); } } + catch (Exception e) { Log.error(e); } + try { if (con != null) { con.close(); } } + catch (Exception e) { Log.error(e); } + } + } +} \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationPolicy.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationPolicy.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/DefaultAuthorizationPolicy.java (revision 2) @@ -0,0 +1,44 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-20 10:46:24 -0500 (Thu, 20 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; +import org.jivesoftware.util.JiveGlobals; + +/** + * This policy will authorize any principal that matches exactly the full + * JID (REALM and server name must be the same if using GSSAPI) or any + * principal that matches exactly the username (without REALM or server + * name). This does exactly what users expect if not supplying a seperate + * principal for authentication. + * + * @author Jay Kline + */ +public class DefaultAuthorizationPolicy implements AuthorizationPolicyProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException { + if (principal.equals(username) || principal.equals(username+"@"+JiveGlobals.getProperty("xmpp.domain"))) { + return; + } else { + throw new UnauthorizedException(); + } + } +} + \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/UnixK5LoginProvider.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/UnixK5LoginProvider.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/UnixK5LoginProvider.java (revision 2) @@ -0,0 +1,126 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-07 09:28:54 -0500 (Fri, 07 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.util.JiveGlobals; +import org.jivesoftware.wildfire.auth.UnauthorizedException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.ArrayList; + + +/** + * Provider for authorization. Checks if the authenticated principal is in + * the user's .k5login file. A traditional Unix Kerberos methodology. The + * location of this file can be configured in the wildfire.xml + * file. An entry in that file would look like the following: + * + *

+ *   <unix>
+ *     <k5login> /home/{0}/.k5login </k5login>
+ *   </unix>
+ * + * The string {0} will be replaced with the username. + * + * @author Jay Kline + */ +public class UnixK5LoginProvider implements AuthorizationProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException { + if( getAuthorized(username).contains(principal) ) { + return; + } else { + throw new UnauthorizedException(); + } + } + + /** + * Returns a String Collection of principals that are authorized to use + * the named user. + * + * @param username The username. + * @return A String Collection of principals that are authorized. + */ + public Collection getAuthorized(String username) { + Collection authorized = new ArrayList(); + try { + String filename = JiveGlobals.getXMLProperty("unix.k5login","/home/{0}/.k5login"); + filename.replace("{0}",username); + File k5login = new File(filename); + FileInputStream fis = new FileInputStream(k5login); + DataInputStream dis = new DataInputStream(fis); + + String line; + while ( (line = dis.readLine() ) != null) { + authorized.add(line); + } + } catch (IOException e) { + //?? + } + return authorized; + } + + /** + * Returns false, this implementation is not writeable. + * + * @return False. + */ + public boolean isWritable() { + return false; + } + + /** + * Always throws UnsupportedOperationException. + * + * @param username The username. + * @param principal The principal authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, String principal) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnauthorizedException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, Collection principals) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void setAuthorized(String username, Collection principals) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + +} \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/StrictAuthorizationPolicy.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/StrictAuthorizationPolicy.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/StrictAuthorizationPolicy.java (revision 2) @@ -0,0 +1,49 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-20 10:46:24 -0500 (Thu, 20 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; +import org.jivesoftware.util.JiveGlobals; + +/** + * This policy will authorize any principal who: + * + *
  • Username of principal matches exactly the username of the JID
  • + *
  • The user principal's realm matches exactly the realm of the server.
  • + * Note that the realm may not match the servername, and in fact for this + * policy to be useful it will not match the servername. RFC3920 Section + * 6.1, item 7 states that if the principal (authorization entity) is the + * same as the JID (initiating entity), its MUST NOT provide an authorization + * identity. In practice however, GSSAPI will provide both. (Note: Ive + * not done extensive testing on this) + * + * @author Jay Kline + */ +public class StrictAuthorizationPolicy implements AuthorizationPolicyProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException { + if(principal.equals(username+"@"+JiveGlobals.getXMLProperty("sasl.realm"))) { + return; + } else { + throw new UnauthorizedException(); + } + } +} + \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/AuthorizationPolicyProvider.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/AuthorizationPolicyProvider.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/AuthorizationPolicyProvider.java (revision 2) @@ -0,0 +1,44 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-20 10:46:24 -0500 (Thu, 20 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; + +/** + * Provider interface for authorization policy. Users that wish to integrate with + * their own authorization system must implement this class and then register + * the implementation with Wildfire in the wildfire.xml + * file. An entry in that file would look like the following: + * + *
    + *   <provider>
    + *     <authorizationpolicy>
    + *       <className>com.foo.auth.CustomPolicyProvider</className>
    + *     </authorizationpolicy>
    + *   </provider>
    + * + * @author Jay Kline + */ +public interface AuthorizationPolicyProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException; + + +} \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/AuthorizationManager.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/AuthorizationManager.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/AuthorizationManager.java (revision 2) @@ -0,0 +1,106 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-07 09:28:54 -0500 (Fri, 07 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; +import org.jivesoftware.util.ClassUtils; +import org.jivesoftware.util.JiveGlobals; +import org.jivesoftware.util.Log; + + +/** + * Manages the AuthorizationProvider object. + * + * @author Jay Kline + */ +public class AuthorizationManager { + + private static AuthorizationProvider provider; + private static AuthorizationPolicyProvider policy; + private static AuthorizationManager instance = new AuthorizationManager(); + + static { + + String a_className = JiveGlobals.getXMLProperty("provider.authorization.className", + "org.jivesoftware.wildfire.sasl.DefaultAuthorizationProvider"); + String p_className = JiveGlobals.getXMLProperty("provider.policy.className"); + try { + Class a_c = ClassUtils.forName(a_className); + provider = (AuthorizationProvider)a_c.newInstance(); + Log.debug("AuthorizationManager: Loaded " + a_className + " AuthorizationProvider"); + } + catch (Exception e) { + Log.error("Error loading authorization provider: " + a_className, e); + provider = new DefaultAuthorizationProvider(); + } + try { + Class p_c = ClassUtils.forName(p_className); + policy = (AuthorizationPolicyProvider)p_c.newInstance(); + Log.debug("AuthorizationManager: Loaded " + p_className + " AuthorizationPolicyProvider"); + } + catch (Exception e) { + Log.error("Error loading authorization policy: " + p_className + ". Using null",e); + policy = null; + } + } + + private AuthorizationManager() { + + } + + /** + * Returns the currently-installed AuthorizationProvider. Warning: You + * should not be calling the AuthorizationProvider directly to perform + * authorizations, it will not take into account the policy selected in + * the wildfire.xml. Use @see{authorize} in this class, instead. + * + * @return the current AuthorizationProvider. + */ + public static AuthorizationProvider getAuthorizationProvider() { + return provider; + } + + /** + * Returns a singleton AuthorizationManager instance. + * + * @return a AuthorizationManager instance. + */ + public static AuthorizationManager getInstance() { + return instance; + } + + /** + * Authorize the authenticated used to the requested username. This uses the + * selected policy and the selected AuthenticationProvider. + * + * @return true if the user is authorized. + */ + + public static boolean authorize(String authorId, String authenId) { + + if(policy != null) { + try { + policy.authorize(authorId,authenId); + return true; + } catch (UnauthorizedException e) { + Log.debug("AuthorizationManager: " + authenId + " rejected by policy"); + } + } + try { + provider.authorize(authorId,authenId); + return true; + } catch (UnauthorizedException e) { + Log.debug("AuthorizationManager: " +authenId + " not authorized to " + authorId + " rejected by provider"); + } + return false; + } +} \ No newline at end of file Index: src/java/org/jivesoftware/wildfire/sasl/AuthorizationProvider.java =================================================================== --- src/java/org/jivesoftware/wildfire/sasl/AuthorizationProvider.java (revision 0) +++ src/java/org/jivesoftware/wildfire/sasl/AuthorizationProvider.java (revision 2) @@ -0,0 +1,91 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: 2006-04-07 09:28:54 -0500 (Fri, 07 Apr 2006) $ + * + * Copyright (C) 2004 Jive Software. All rights reserved. + * + * This software is published under the terms of the GNU Public License (GPL), + * a copy of which is included in this distribution. + */ + +package org.jivesoftware.wildfire.sasl; + +import org.jivesoftware.wildfire.auth.UnauthorizedException; + +import java.util.Collection; + +/** + * Provider interface for authorization. Users that wish to integrate with + * their own authorization system must implement this class and then register + * the implementation with Wildfire in the wildfire.xml + * file. An entry in that file would look like the following: + * + *
    + *   <provider>
    + *     <authorization>
    + *       <className>com.foo.auth.CustomAuthProvider</className>
    + *     </authorization>
    + *   </provider>
    + * + * @author Jay Kline + */ +public interface AuthorizationProvider { + + /** + * Returns if the principal is explicity authorized to the JID, throws + * an UnauthorizedException otherwise + * + * @param username The username requested. + * @param principal The principal requesting the username. + * @throws UnauthorizedException + */ + public void authorize(String username, String principal) throws UnauthorizedException; + + /** + * Returns a String Collection of principals that are authorized to use + * the named user. + * + * @param username The username. + * @return A String Collection of principals that are authorized. + */ + public Collection getAuthorized(String username); + + /** + * Returns true if this AuthorizationProvider supports changing the + * list of authorized principals for users. + * + * @return true if updating the list of authorized principals is + * supported by this AuthorizationProvider. + */ + public boolean isWritable(); + + /** + * Add a single authorized principal to use the named user. + * + * @param username The username. + * @param principal The principal authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, String principal) throws UnsupportedOperationException; + + /** + * Add a Collection of users authorized to use the named user. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void addAuthorized(String username, Collection principals) throws UnsupportedOperationException; + + /** + * Set the users authorized to use the named user. All existing principals listed + * will be removed. + * + * @param username The username. + * @param principals The Collection of principals authorized to use the named user. + * @throws UnsupportedOperationException If this AuthorizationProvider cannot be updated. + */ + public void setAuthorized(String username, Collection principals) throws UnsupportedOperationException; + +} \ No newline at end of file