/* * Copyright (C) 2004-2008 Jive Software. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.openfire.auth; import java.util.StringTokenizer; import java.util.Vector; import org.jivesoftware.openfire.admin.AdminManager; import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Different clients perform authentication differently, so this policy * will authorize any principal to a requested user that match specific * conditions that are considered secure defaults for most installations. * * Keep in mind if a client does not request any username Java copies the * authenticated ID to the requested username. * * <ul> * <li>If the authenticated ID is in the form of a plain username, and the * requested user is in the form of a plain username, then the two must * be exactly the same. * <li>If the authenticated ID contains an '@', then the portion before the * '@' must match exactly the requested username and the portion after * the '@' must match at least one of the following: * <ul> * <li>The XMPP domain of the server * <li>The SASL realm of the server * <li>Be in the list of acceptable realms * </ul> * <li>If the requested username contains an '@' then the portion before the * '@' will be considered the requested username only if the portion after * the '@' matches the XMPP domain of the server or the portion after the * '@' in the authenticated ID, if any. * </ul> * * * @see AuthorizationManager * @author Jay Kline */ public class DefaultAuthorizationPolicy implements AuthorizationPolicy { private static final Logger Log = LoggerFactory.getLogger(DefaultAuthorizationPolicy.class); private Vector<String> approvedRealms; private boolean proxyAuth; public DefaultAuthorizationPolicy() { approvedRealms = new Vector<>(); proxyAuth = false; String realmList = JiveGlobals.getProperty("sasl.approvedRealms"); if(realmList != null) { StringTokenizer st = new StringTokenizer(realmList, " ,\t\n\r\f"); while(st.hasMoreTokens()) { approvedRealms.add(st.nextToken()); } } proxyAuth = JiveGlobals.getBooleanProperty("sasl.proxyAuth", false); } /** * Returns true if the principal is explicitly authorized to the JID * * @param username The username requested. * @param authenID The authenticated ID (principal) requesting the username. * @return true if the authenticated ID is authorized to the requested user. */ @Override public boolean authorize(String username, String authenID) { boolean authorized = false; String userUser = username; //I know, I know, dumb variable name... String userRealm = null; String authenUser = authenID; String authenRealm = null; if(username.contains("@")) { userUser = username.substring(0,username.lastIndexOf("@")); userRealm = username.substring((username.lastIndexOf("@")+1)); } if(authenID.contains("@")){ authenUser = authenID.substring(0,(authenID.lastIndexOf("@"))); authenRealm = authenID.substring((authenID.lastIndexOf("@")+1)); } if (!proxyAuth || !AdminManager.getInstance().isUserAdmin(authenUser, true)) { if(!userUser.equals(authenUser)) { //for this policy the user portion of both must match, so lets short circut here if we can if(JiveGlobals.getBooleanProperty("xmpp.auth.ignorecase",true)) { if(!userUser.toLowerCase().equals(authenUser.toLowerCase())){ if (Log.isDebugEnabled()) { Log.debug("DefaultAuthorizationPolicy: usernames don't match ("+userUser+" "+authenUser+")"); } return false; } } else { Log.debug("DefaultAuthorizationPolicy: usernames don't match ("+userUser+" "+authenUser+")"); return false; } } } Log.debug("DefaultAuthorizationPolicy: Checking authenID realm"); // Next up, check if the authenID realm is acceptable. if(authenRealm != null) { if(authenRealm.equals(JiveGlobals.getProperty("xmpp.domain"))) { Log.debug("DefaultAuthorizationPolicy: authenRealm = xmpp.domain"); authorized = true; } else if(authenRealm.equals(JiveGlobals.getProperty("sasl.realm"))) { Log.debug("DefaultAuthorizationPolicy: authenRealm = sasl.realm"); authorized = true; } else { for(String realm : approvedRealms) { if(authenRealm.equals(realm)) { if (Log.isDebugEnabled()) { Log.debug("DefaultAuthorizationPolicy: authenRealm = "+realm+" which is approved"); } authorized = true; } else { if (Log.isDebugEnabled()) { Log.debug("DefaultAuthorizationPolicy: authenRealm != "+realm+" which is approved"); } } } } } else { //no realm in the authenID authorized = true; } if(!authorized) { return false; } else { //reset for next round of tests authorized = false; } //Next up, check if the username realm is acceptable. if(userRealm != null) { if(userRealm.equals(JiveGlobals.getProperty("xmpp.domain"))) { Log.debug("DefaultAuthorizationPolicy: userRealm = xmpp.domain"); authorized = true; } else { if(authenRealm != null && authenRealm.equals(userRealm)) { //authen and username are identical if (Log.isDebugEnabled()) { Log.debug("DefaultAuthorizationPolicy: userRealm = "+authenRealm+" which is approved"); } authorized = true; } } } else { authorized = true; } //no more checks return authorized; } /** * Returns the short name of the Policy * * @return The short name of the Policy */ @Override public String name() { return "Default Policy"; } /** * Returns a description of the Policy * * @return The description of the Policy. */ @Override public String description() { return "Different clients perform authentication differently, so this policy "+ "will authorize any principal to a requested user that match specific "+ "conditions that are considered secure defaults for most installations."; } }