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.Packet;
026import org.jivesoftware.smack.util.StringUtils;
027
028/**
029 * Filters for packets which are a valid reply to an IQ request.
030 * <p>
031 * Such a packet must have the same packet id and must be an IQ 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 PacketFilter {
052    private static final Logger LOGGER = Logger.getLogger(IQReplyFilter.class.getName());
053
054    private final PacketFilter 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 packet must have the same packet id and must be an IQ 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        to = iqPacket.getTo();
085        if (conn.getUser() == null) {
086            // We have not yet been assigned a username, this can happen if the connection is
087            // in an early stage, i.e. when performing the SASL auth.
088            local = null;
089        } else {
090            local = conn.getUser().toLowerCase(Locale.US);
091        }
092        server = conn.getServiceName().toLowerCase(Locale.US);
093        packetId = iqPacket.getPacketID();
094
095        PacketFilter iqFilter = new OrFilter(new IQTypeFilter(IQ.Type.ERROR), new IQTypeFilter(IQ.Type.RESULT));
096        PacketFilter idFilter = new PacketIDFilter(iqPacket);
097        iqAndIdFilter = new AndFilter(iqFilter, idFilter);
098        fromFilter = new OrFilter();
099        fromFilter.addFilter(FromMatchesFilter.createFull(to));
100        if (to == null) {
101            if (local != null)
102                fromFilter.addFilter(FromMatchesFilter.createBare(local));
103            fromFilter.addFilter(FromMatchesFilter.createFull(server));
104        }
105        else if (local != null && to.toLowerCase(Locale.US).equals(StringUtils.parseBareAddress(local))) {
106            fromFilter.addFilter(FromMatchesFilter.createFull(null));
107        }
108    }
109
110    @Override
111    public boolean accept(Packet packet) {
112        // First filter out everything that is not an IQ stanza and does not have the correct ID set.
113        if (!iqAndIdFilter.accept(packet))
114            return false;
115
116        // Second, check if the from attributes are correct and log potential IQ spoofing attempts
117        if (fromFilter.accept(packet)) {
118            return true;
119        } else {
120            String msg = String.format("Rejected potentially spoofed reply to IQ-packet. Filter settings: "
121                            + "packetId=%s, to=%s, local=%s, server=%s. Received packet with from=%s",
122                            packetId, to, local, server, packet.getFrom());
123            LOGGER.log(Level.WARNING, msg , packet);
124            return false;
125        }
126    }
127
128}