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.logging.Level; 020import java.util.logging.Logger; 021 022import org.jivesoftware.smack.XMPPConnection; 023import org.jivesoftware.smack.packet.IQ; 024import org.jivesoftware.smack.packet.Stanza; 025 026import org.jxmpp.jid.DomainBareJid; 027import org.jxmpp.jid.EntityFullJid; 028import org.jxmpp.jid.Jid; 029 030/** 031 * Filters for packets which are a valid reply to an IQ request. 032 * <p> 033 * Such a stanza must have the same stanza id and must be an IQ stanza of type 034 * <code>RESULT</code> or <code>ERROR</code>. Moreover, it is necessary to check 035 * the <code>from</code> address to ignore forged replies. 036 * <p> 037 * We accept a <code>from</code> address if one of the following is true: 038 * <ul> 039 * <li>It matches the <code>to</code> address of the request. 040 * <li>The <code>to</code> address of the request was empty and the 041 * <code>from</code> address matches either the bare jid of the server or the 042 * (bare or full jid) of the client. 043 * <li>To <code>to</code> was our bare address and the <code>from</code> is empty. 044 * </ul> 045 * <p> 046 * For a discussion of the issues, see the thread "Spoofing of iq ids and 047 * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list 048 * and following discussion in February and March. 049 * 050 * @author Lars Noschinski 051 * 052 */ 053public class IQReplyFilter implements StanzaFilter { 054 private static final Logger LOGGER = Logger.getLogger(IQReplyFilter.class.getName()); 055 056 private final StanzaFilter iqAndIdFilter; 057 private final OrFilter fromFilter; 058 private final Jid to; 059 private final EntityFullJid local; 060 private final DomainBareJid server; 061 private final String packetId; 062 063 /** 064 * Filters for packets which are a valid reply to an IQ request. 065 * <p> 066 * Such a stanza must have the same stanza id and must be an IQ stanza of type 067 * <code>RESULT</code> or <code>ERROR</code>. Moreover, it is necessary to check 068 * the <code>from</code> address to ignore forged replies. 069 * <p> 070 * We accept a <code>from</code> address if one of the following is true: 071 * <ul> 072 * <li>It matches the <code>to</code> address of the request. 073 * <li>The <code>to</code> address of the request was empty and the 074 * <code>from</code> address matches either the bare jid of the server or the 075 * (bare or full jid) of the client. 076 * <li>To <code>to</code> was our bare address and the <code>from</code> is empty. 077 * </ul> 078 * <p> 079 * For a discussion of the issues, see the thread "Spoofing of iq ids and 080 * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list 081 * and following discussion in February and March. 082 * 083 * @param iqPacket An IQ request. Filter for replies to this packet. 084 * @param conn connection. 085 */ 086 public IQReplyFilter(IQ iqPacket, XMPPConnection conn) { 087 if (!iqPacket.isRequestIQ()) { 088 throw new IllegalArgumentException("IQ must be a request IQ, i.e. of type 'get' or 'set'."); 089 } 090 to = iqPacket.getTo(); 091 local = conn.getUser(); 092 if (local == null) { 093 throw new IllegalArgumentException("Must have a local (user) JID set. Either you didn't configure one or you where not connected at least once"); 094 } 095 096 server = conn.getXMPPServiceDomain(); 097 packetId = iqPacket.getStanzaId(); 098 099 StanzaFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT); 100 StanzaFilter idFilter = new StanzaIdFilter(iqPacket); 101 iqAndIdFilter = new AndFilter(iqFilter, idFilter); 102 fromFilter = new OrFilter(); 103 fromFilter.addFilter(FromMatchesFilter.createFull(to)); 104 if (to == null) { 105 fromFilter.addFilter(FromMatchesFilter.createBare(local)); 106 fromFilter.addFilter(FromMatchesFilter.createFull(server)); 107 } 108 else if (to.equals(local.asBareJid())) { 109 fromFilter.addFilter(FromMatchesFilter.createFull(null)); 110 } 111 } 112 113 @Override 114 public boolean accept(Stanza packet) { 115 // First filter out everything that is not an IQ stanza and does not have the correct ID set. 116 if (!iqAndIdFilter.accept(packet)) 117 return false; 118 119 // Second, check if the from attributes are correct and log potential IQ spoofing attempts 120 if (fromFilter.accept(packet)) { 121 return true; 122 } else { 123 String msg = String.format("Rejected potentially spoofed reply to IQ-packet. Filter settings: " 124 + "packetId=%s, to=%s, local=%s, server=%s. Received packet with from=%s", 125 packetId, to, local, server, packet.getFrom()); 126 LOGGER.log(Level.WARNING, msg , packet); 127 return false; 128 } 129 } 130 131 @Override 132 public String toString() { 133 StringBuilder sb = new StringBuilder(); 134 sb.append(getClass().getSimpleName()); 135 sb.append(": iqAndIdFilter (").append(iqAndIdFilter.toString()).append("), "); 136 sb.append(": fromFilter (").append(fromFilter.toString()).append(')'); 137 return sb.toString(); 138 } 139}