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}