#!/usr/bin/env python
#-------------------------------------------------------------------------------
# Name:         PluginSessionRenegotiation.py
# Purpose:      Tests the target server for insecure renegotiation.
#
# Author:       alban
#
# Copyright:    2012 SSLyze developers
#
#   SSLyze is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 2 of the License, or
#   (at your option) any later version.
#
#   SSLyze is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with SSLyze.  If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------

import socket
from xml.etree.ElementTree import Element

from plugins import PluginBase
from utils.SSLyzeSSLConnection import create_sslyze_connection
from nassl._nassl import OpenSSLError


class PluginSessionRenegotiation(PluginBase.PluginBase):

    interface = PluginBase.PluginInterface("PluginSessionRenegotiation",  "")
    interface.add_command(
        command="reneg",
        help=(
            "Tests the target server's support for client-initiated "
            'renegotiations and secure renegotiations.'),
        dest=None)


    def process_task(self, target, command, args):

        (clientReneg, secureReneg) = self._test_renegotiation(target)
        
        # Text output
        clientTxt = 'Honored' if clientReneg else 'Rejected'
        secureTxt = 'Supported' if secureReneg else 'Not supported'
        cmdTitle = 'Session Renegotiation'
        txtOutput = [self.PLUGIN_TITLE_FORMAT(cmdTitle)]
        
        outFormat = '      {0:<35} {1}'.format
        txtOutput.append(outFormat('Client-initiated Renegotiations:', clientTxt))
        txtOutput.append(outFormat('Secure Renegotiation: ', secureTxt))
        
        # XML output
        xmlReneg = Element('sessionRenegotiation', 
                           attrib = {'canBeClientInitiated' : str(clientReneg),
                                     'isSecure' : str(secureReneg)})
        
        xmlOutput = Element(command, title=cmdTitle)
        xmlOutput.append(xmlReneg)
        
        return PluginBase.PluginResult(txtOutput, xmlOutput)


    def _test_renegotiation(self, target):
        """
        Checks whether the server honors session renegotiation requests and 
        whether it supports secure renegotiation.
        """
        
        sslConn = create_sslyze_connection(self._shared_settings)
        
        try: # Perform the SSL handshake
            sslConn.connect((target[0], target[2]))
            secureReneg = sslConn.get_secure_renegotiation_support()
    
            try: # Let's try to renegotiate
                sslConn.do_renegotiate()
                clientReneg = True
    
            # Errors caused by a server rejecting the renegotiation
            except IOError as e:
                if 'Nassl SSL handshake failed' in str(e.args):
                    clientReneg = False
                else:
                    raise
            except socket.error as e:
                if 'connection was forcibly closed' in str(e.args):
                    clientReneg = False
                elif 'reset by peer' in str(e.args):
                    clientReneg = False
                else:
                    raise
            #except socket.timeout as e:
            #    result_reneg = 'Rejected (timeout)'
            except OpenSSLError as e:
                if 'handshake failure' in str(e.args):
                    clientReneg = False
                elif 'no renegotiation' in str(e.args):
                    clientReneg = False
                else:
                    raise
    
        finally:
            sslConn.close()
    
        return (clientReneg, secureReneg)

@@ -3101,265 +3101,8 @@
 rue%0A
-    %0A            # Errors caused by a server rejecting the renegotiation%0A            except IOError as e:%0A                if 'Nassl SSL handshake failed' in str(e.args):%0A                    clientReneg = False%0A                else:%0A                    raise
 %0A   
@@ -3752,24 +3752,357 @@
       raise%0A
+                %0A            # Errors caused by a server rejecting the renegotiation%0A            # Should be last as socket errors are also IOError%0A            except IOError as e:%0A                if 'Nassl SSL handshake failed' in str(e.args):%0A                    clientReneg = False%0A                else:%0A                    raise%0A
     %0A       

Handle IOError last