############################################################################
##
## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary
## All rights reserved.
##
## $Id: Session.py,v 1.22.2.3.2.3 2001/09/19 19:22:17 bazsi Exp $
##
## Author  : Bazsi
## Auditor : kisza
## Last version: 1.9
## Notes:
##   - comments in English
##
############################################################################

"""Module defining session related classes and functions. 

This module defines the abstract session interface in a class named
'AbstractSession', and two descendants 'MasterSession' and 'StackedSession'.

Sessions are hierarchically stacked into each other just like proxies.
Each session has an owner session (except for the master session which
is on the top), and variables are "inherited" from owner sessions. 
(implemented using a simple getattr wrapper) This way stacked sessions
can inherit data from encapsulating proxies. (an HTTP proxy may define an
URL and a mime-type, and stacked CVP module may inspect those values)
"""

import Zorp
from Zorp import *
from Zone import root_zone

class AbstractSession:
	"""Abstract base class for different session types (master, or stacked)

	Abstract base class for different session types (master, or stacked), 
	both MasterSession and StackedSession are derived from this class.
	
	Attributes
	
	  none
	
	"""

	def destroy(self):
		"""Destroys the session. 
		
		We close filedescriptors here, in case no proxy
		module could be started (because of policy violations,
		or because the module cannot be found).
		
		Arguments
		
		  self -- this instance
		"""
		if self.client_fd != -1:
			closeFd(self.client_fd)
		if self.server_fd != -1:
			closeFd(self.server_fd)


class MasterSession(AbstractSession):
	"""Class encapsulating a master session.
	
	This class encapsulates a master session, which means
	that it's on the top of the session hierarchy.
	
	Attributes
	
	  client_fd      -- client fd
	  
	  client_stream  -- client stream
	  
	  client_address -- SockAddr instance containing client address
	  
	  client_local   -- local address (on the firewall)
	  
	  client_dest    -- client original destination (before redirected to Zorp)
	  
	  client_zone    -- zone of the client

	  server_fd      -- server fd
	  
	  server_stream  -- server stream
	  
	  server_address -- SockAddr instance containing server address

	  server_address_inband -- destination address is determined inband the channel
	  
	  server_local   -- local address (on the firewall)
	  
	  server_zone    -- zone of the server

	  service        -- service instance this session runs
	  
	  session_id     -- unique identifier for this session in the
	                    format: "(firewall/service:instance id/proxy)"
	  
	  instance_id    -- the instance identifier of the service
	                    (sequence number)
	                    
	  started        -- indicates that the instance has been started

	  auth           -- authentication method
	  
	"""
	
	def __init__(self):
		"""Constructor to initialize a MasterSession instance.

		This constructor initializes a new MasterSession instance
		based on its arguments.

		Arguments
		
		  self -- this instance
		
		"""
		self.session_id = "(%s)" % Zorp.firewall_name,
		
		self.client_stream = None
		self.client_fd = -1
		self.client_address = None
		self.client_local = None
		self.client_dest = None
		self.client_zone = None
		
		self.server_stream = None
		self.server_fd = -1
		self.server_address = None
		self.server_address_inband = 0
		self.server_local = None
		self.server_zone = None
		
	        self.auth = None
      	        self.auth_user = ""
	
		self.started = 0
		self.service = None
		self.instance_id = 0

	def __del__(self):
		"""Function called when the master session is freed.
		
		This function is called when the master session is freed,
		thus the session ended. We inform our spawner service
		about this event.
		
		Arguments
		
		  self -- this instance
		"""
		self.service.stopInstance(self)

	def setService(self, service):
		"""Sets the service belonging to this session.

		Stores the service reference, and recalculates the session_id.
		This is called by the Listener after the service is determined.
		
		Arguments

		  self -- this instance
		  
		  service  -- Service instance
		"""
		self.service = service
		self.session_id = "(%s/%s)" % (Zorp.firewall_name, service.name)

	def setClient(self, fd, addr):
		"""Set client address and perform access control.
		
		Sets the client address of the given session, and performs
		access control checks.
                   
		Arguments

		  self   -- this instance
		
		  fd     -- fd of the client
		  
		  addr   -- sockaddr of the client
		"""
		self.client_fd = fd
		self.client_address = addr
		self.client_zone = root_zone.findZone(addr)

	def isClientPermitted(self):
		"""Function to actually check access control.
		
		This function is called when a connection is established to
		perform access control checks whether the client is
		permitted to use the requested service. Its return value
		specifies the result of the check.
		
		Arguments
		
		  self -- this instance
		  
		Returns
		
		  Z_ACCEPT for success, and Z_REJECT for failure.
		"""
		if self.client_zone.isOutboundServicePermitted(self) != Z_ACCEPT:
			message(0, "%s: Outbound service %s not permitted from zone %s" % (self.session_id, self.service, self.client_zone))
			return Z_REJECT
		return Z_ACCEPT

	def setServer(self, addr):
		"""Set the server address and perform access control checks.
		
		Stores the server address of the given connection, looks
		up server zone and performs access control and raises an
		exception upon failure.

		Arguments

		  self -- this instance
		  
		  addr -- Server address

		"""
		self.server_address = addr
		self.server_zone = root_zone.findZone(addr)
		
	def isServerPermitted(self):
		"""Function to actually check access control.
		
		This function is called when a connection is to be
		established with the server. It performs access control
		checks whether the connection to the server is permitted by
		the policy.  Its return value specifies the result of the
		check.
		
		Arguments
		
		  self -- this instance
		  
		Returns
		
		  Z_ACCEPT for success, and Z_REJECT for failure.
		"""
		if self.server_zone.isInboundServicePermitted(self) != Z_ACCEPT:
			message(0, "%s: Inbound service %s not permitted into zone %s" % (self.session_id, self.service, self.server_zone))
			return Z_REJECT
		return Z_ACCEPT
	
	def setServiceInstance(self, instance_id):
		"""Set service instance number and recalculate session id.
		
		Sets service instance number, and makes up a unique
		identifier for this session.

		Arguments

		  self -- this instance
		  
		  instance_id -- unique identifier of the service instance

		"""
		self.instance_id = instance_id
		self.session_id = "(%s/%s:%d)" % (Zorp.firewall_name, self.name, self.instance_id)

class StackedSession(AbstractSession):
	"""Session class for subsessions.
	
	A StackedSession is a subsession, inheriting attributes from
	its parent.
	
	Attributes
	
	  owner     -- Parent session
	  
	  chainer   -- Chainer used to chain up to parent. If none
	               simply server_fd is used.
	"""

	def __init__(self, owner, chainer = None):
                """Constructor to initialize a StackedSession instance.

		This constructor initializes a new StackedSession instance
		based on parameters.
		
		Arguments

		  self        --    this instance
		  
		  owner       --    Parent session
		  
		  chainer     --    Chainer used to chain up to parent.
                """
		self.owner = owner
		self.chainer = chainer
		
	def __getattr__(self, name):
		"""Function to perform attribute inheritance.
		
		This function is called by the Python core when an attribute
		is referenced. It returns variables from the parent session, if
		not overriden here.

		Arguments

		  self         --   this instance
		  
		  name         --   Name of the attribute to get.

		Returns
		
		  The value of the given attribute.

		"""
		try:
			return self.__dict__[name]
		except KeyError:
			return getattr(self.owner, name)

	def setProxy(self, proxy):
		"""Set the proxy name used in this subsession.

		Stores a reference to the proxy class, and modifies
		the session_id to include the proxy name. This is
		called by the Listener after the proxy module to 
		use is determined.
		
		Arguments

		  self       --     this instance
		  
		  proxy      --     Proxy class, derived from Proxy

                """
		self.session_id = "(%s/%s:%d/%s)" % (Zorp.firewall_name, self.name, self.instance_id, proxy)

