/** * * Copyright 2014 Lars Noschinski * * 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.smack.filter; import java.util.logging.Level; import java.util.logging.Logger; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Stanza; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.Jid; /** * Filters for packets which are a valid reply to an IQ request. *

* Such a stanza(/packet) must have the same stanza(/packet) id and must be an IQ stanza(/packet) of type * RESULT or ERROR. Moreover, it is necessary to check * the from address to ignore forged replies. *

* We accept a from address if one of the following is true: *

*

* For a discussion of the issues, see the thread "Spoofing of iq ids and * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list * and following discussion in February and March. * * @author Lars Noschinski * */ public class IQReplyFilter implements StanzaFilter { private static final Logger LOGGER = Logger.getLogger(IQReplyFilter.class.getName()); private final StanzaFilter iqAndIdFilter; private final OrFilter fromFilter; private final Jid to; private final EntityFullJid local; private final DomainBareJid server; private final String packetId; /** * Filters for packets which are a valid reply to an IQ request. *

* Such a stanza(/packet) must have the same stanza(/packet) id and must be an IQ stanza(/packet) of type * RESULT or ERROR. Moreover, it is necessary to check * the from address to ignore forged replies. *

* We accept a from address if one of the following is true: *

*

* For a discussion of the issues, see the thread "Spoofing of iq ids and * misbehaving servers" from 2014-01 on the jdev@jabber.org mailing list * and following discussion in February and March. * * @param iqPacket An IQ request. Filter for replies to this packet. */ public IQReplyFilter(IQ iqPacket, XMPPConnection conn) { if (!iqPacket.isRequestIQ()) { throw new IllegalArgumentException("IQ must be a request IQ, i.e. of type 'get' or 'set'."); } to = iqPacket.getTo(); local = conn.getUser(); if (local == null) { throw new IllegalArgumentException("Must have a local (user) JID set. Either you didn't configure one or you where not connected at least once"); } server = conn.getXMPPServiceDomain(); packetId = iqPacket.getStanzaId(); StanzaFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT); StanzaFilter idFilter = new StanzaIdFilter(iqPacket); iqAndIdFilter = new AndFilter(iqFilter, idFilter); fromFilter = new OrFilter(); fromFilter.addFilter(FromMatchesFilter.createFull(to)); if (to == null) { fromFilter.addFilter(FromMatchesFilter.createBare(local)); fromFilter.addFilter(FromMatchesFilter.createFull(server)); } else if (to.equals(local.asBareJid())) { fromFilter.addFilter(FromMatchesFilter.createFull(null)); } } @Override public boolean accept(Stanza packet) { // First filter out everything that is not an IQ stanza and does not have the correct ID set. if (!iqAndIdFilter.accept(packet)) return false; // Second, check if the from attributes are correct and log potential IQ spoofing attempts if (fromFilter.accept(packet)) { return true; } else { String msg = String.format("Rejected potentially spoofed reply to IQ-packet. Filter settings: " + "packetId=%s, to=%s, local=%s, server=%s. Received packet with from=%s", packetId, to, local, server, packet.getFrom()); LOGGER.log(Level.WARNING, msg , packet); return false; } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(": iqAndIdFilter (").append(iqAndIdFilter.toString()).append("), "); sb.append(": fromFilter (").append(fromFilter.toString()).append(')'); return sb.toString(); } }