001/** 002 * 003 * Copyright 2014 Lars Noschinski 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.filter; 018 019import java.util.Locale; 020import java.util.logging.Level; 021import java.util.logging.Logger; 022 023import org.jivesoftware.smack.XMPPConnection; 024import org.jivesoftware.smack.packet.IQ; 025import org.jivesoftware.smack.packet.Stanza; 026import org.jxmpp.util.XmppStringUtils; 027 028/** 029 * Filters for packets which are a valid reply to an IQ request. 030 * <p> 031 * Such a stanza(/packet) must have the same stanza(/packet) id and must be an IQ stanza(/packet) of type 032 * <code>RESULT</code> or <code>ERROR</code>. Moreover, it is necessary to check 033 * the <code>from</code> address to ignore forged replies. 034 * <p> 035 * We accept a <code>from</code> address if one of the following is true: 036 * <ul> 037 * <li>It matches the <code>to</code> address of the request. 038 * <li>The <code>to</code> address of the request was empty and the 039 * <code>from</code> address matches either the bare jid of the server or the 040 * (bare or full jid) of the client. 041 * <li>To <code>to</code> was our bare address and the <code>from</code> is empty. 042 * </ul> 043 * <p> 044 * For a discussion of the issues, see the thread "Spoofing of iq ids and 045 * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list 046 * and following discussion in February and March. 047 * 048 * @author Lars Noschinski 049 * 050 */ 051public class IQReplyFilter implements StanzaFilter { 052 private static final Logger LOGGER = Logger.getLogger(IQReplyFilter.class.getName()); 053 054 private final StanzaFilter iqAndIdFilter; 055 private final OrFilter fromFilter; 056 private final String to; 057 private final String local; 058 private final String server; 059 private final String packetId; 060 061 /** 062 * Filters for packets which are a valid reply to an IQ request. 063 * <p> 064 * Such a stanza(/packet) must have the same stanza(/packet) id and must be an IQ stanza(/packet) of type 065 * <code>RESULT</code> or <code>ERROR</code>. Moreover, it is necessary to check 066 * the <code>from</code> address to ignore forged replies. 067 * <p> 068 * We accept a <code>from</code> address if one of the following is true: 069 * <ul> 070 * <li>It matches the <code>to</code> address of the request. 071 * <li>The <code>to</code> address of the request was empty and the 072 * <code>from</code> address matches either the bare jid of the server or the 073 * (bare or full jid) of the client. 074 * <li>To <code>to</code> was our bare address and the <code>from</code> is empty. 075 * </ul> 076 * <p> 077 * For a discussion of the issues, see the thread "Spoofing of iq ids and 078 * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list 079 * and following discussion in February and March. 080 * 081 * @param iqPacket An IQ request. Filter for replies to this packet. 082 */ 083 public IQReplyFilter(IQ iqPacket, XMPPConnection conn) { 084 if (!iqPacket.isRequestIQ()) { 085 throw new IllegalArgumentException("IQ must be a request IQ, i.e. of type 'get' or 'set'."); 086 } 087 if (iqPacket.getTo() != null) { 088 to = iqPacket.getTo().toLowerCase(Locale.US); 089 } else { 090 to = null; 091 } 092 final String localJid = conn.getUser(); 093 if (localJid == null) { 094 throw new IllegalArgumentException("Must have a local (user) JID set. Either you didn't configure one or you where not connected at least once"); 095 } 096 local = localJid.toLowerCase(Locale.US); 097 098 server = conn.getServiceName().toLowerCase(Locale.US); 099 packetId = iqPacket.getStanzaId(); 100 101 StanzaFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT); 102 StanzaFilter idFilter = new StanzaIdFilter(iqPacket); 103 iqAndIdFilter = new AndFilter(iqFilter, idFilter); 104 fromFilter = new OrFilter(); 105 fromFilter.addFilter(FromMatchesFilter.createFull(to)); 106 if (to == null) { 107 fromFilter.addFilter(FromMatchesFilter.createBare(local)); 108 fromFilter.addFilter(FromMatchesFilter.createFull(server)); 109 } 110 else if (to.equals(XmppStringUtils.parseBareJid(local))) { 111 fromFilter.addFilter(FromMatchesFilter.createFull(null)); 112 } 113 } 114 115 @Override 116 public boolean accept(Stanza packet) { 117 // First filter out everything that is not an IQ stanza and does not have the correct ID set. 118 if (!iqAndIdFilter.accept(packet)) 119 return false; 120 121 // Second, check if the from attributes are correct and log potential IQ spoofing attempts 122 if (fromFilter.accept(packet)) { 123 return true; 124 } else { 125 String msg = String.format("Rejected potentially spoofed reply to IQ-packet. Filter settings: " 126 + "packetId=%s, to=%s, local=%s, server=%s. Received packet with from=%s", 127 packetId, to, local, server, packet.getFrom()); 128 LOGGER.log(Level.WARNING, msg , packet); 129 return false; 130 } 131 } 132 133 @Override 134 public String toString() { 135 StringBuilder sb = new StringBuilder(); 136 sb.append(getClass().getSimpleName()); 137 sb.append(": iqAndIdFilter (").append(iqAndIdFilter.toString()).append("), "); 138 sb.append(": fromFilter (").append(fromFilter.toString()).append(')'); 139 return sb.toString(); 140 } 141}