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