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