예제 #1
0
 def test_get_elements(self):
     # Test that we can handle SOAP-level error messages
     # TODO: The request actually raises ErrorInvalidRequest, but we interpret that to mean a wrong API version and
     #  end up throwing ErrorInvalidServerVersion. We should make a more direct test.
     svc = ResolveNames(self.account.protocol)
     with self.assertRaises(ErrorInvalidServerVersion):
         list(svc._get_elements(create_element("XXX")))
예제 #2
0
    def test_resolvenames_parsing(self):
        # Test static XML since server has no roomlists
        ws = ResolveNames(self.account.protocol)
        xml = b'''\
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo
        MajorVersion="15" MinorVersion="0" MajorBuildNumber="1293" MinorBuildNumber="4" Version="V2_23"
        xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  </s:Header>
  <s:Body>
    <m:ResolveNamesResponse
            xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
            xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Warning">
          <m:MessageText>Multiple results were found.</m:MessageText>
          <m:ResponseCode>ErrorNameResolutionMultipleResults</m:ResponseCode>
          <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
          <m:ResolutionSet TotalItemsInView="2" IncludesLastItemInRange="true">
            <t:Resolution>
              <t:Mailbox>
                <t:Name>John Doe</t:Name>
                <t:EmailAddress>[email protected]</t:EmailAddress>
                <t:RoutingType>SMTP</t:RoutingType>
                <t:MailboxType>Mailbox</t:MailboxType>
              </t:Mailbox>
            </t:Resolution>
            <t:Resolution>
              <t:Mailbox>
                <t:Name>John Deer</t:Name>
                <t:EmailAddress>[email protected]</t:EmailAddress>
                <t:RoutingType>SMTP</t:RoutingType>
                <t:MailboxType>Mailbox</t:MailboxType>
              </t:Mailbox>
            </t:Resolution>
          </m:ResolutionSet>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </m:ResolveNamesResponse>
  </s:Body>
</s:Envelope>'''
        header, body = ws._get_soap_parts(response=MockResponse(xml))
        res = ws._get_elements_in_response(response=ws._get_soap_messages(
            body=body))
        self.assertSetEqual(
            {
                Mailbox.from_xml(elem=elem.find(Mailbox.response_tag()),
                                 account=None).email_address
                for elem in res
            }, {'*****@*****.**', '*****@*****.**'})
예제 #3
0
 def test_exceeded_connection_count(self):
     # Test server repeatedly returning ErrorExceededConnectionCount
     svc = ResolveNames(self.account.protocol)
     tmp = svc._get_soap_messages
     try:
         # We need to fail fast so we don't end up in an infinite loop
         svc._get_soap_messages = Mock(
             side_effect=ErrorExceededConnectionCount("XXX"))
         with self.assertRaises(ErrorExceededConnectionCount) as e:
             list(svc.call(unresolved_entries=["XXX"]))
         self.assertEqual(e.exception.args[0], "XXX")
     finally:
         svc._get_soap_messages = tmp
예제 #4
0
 def test_handle_backoff(self):
     # Test that we can handle backoff messages
     svc = ResolveNames(self.account.protocol)
     tmp = svc._response_generator
     orig_policy = self.account.protocol.config.retry_policy
     try:
         # We need to fail fast so we don't end up in an infinite loop
         self.account.protocol.config.retry_policy = FailFast()
         svc._response_generator = Mock(
             side_effect=ErrorServerBusy("XXX", back_off=1))
         with self.assertRaises(ErrorServerBusy) as e:
             list(svc._get_elements(create_element("XXX")))
         self.assertEqual(e.exception.args[0], "XXX")
     finally:
         svc._response_generator = tmp
         self.account.protocol.config.retry_policy = orig_policy
예제 #5
0
def gal(dump, search, verbose):
    """
        Dump GAL using EWS.
        The slower technique used by https://github.com/dafthack/MailSniper
        default searches from "aa" to "zz" and prints them all.
        EWS only returns batches of 100
        There will be doubles, so uniq after.
    """

    if verbose:
        logging.basicConfig(level=logging.DEBUG, handlers=[PrettyXmlHandler()])

    credentials = Credentials(tbestate.username, tbestate.password)
    username = tbestate.username

    if tbestate.exch_host:
        config = Configuration(server=tbestate.exch_host,
                               credentials=credentials)
        account = Account(username,
                          config=config,
                          autodiscover=False,
                          access_type=DELEGATE)
    else:
        account = Account(username,
                          credentials=credentials,
                          autodiscover=True,
                          access_type=DELEGATE)

    atoz = [
        ''.join(x) for x in itertools.product(string.ascii_lowercase, repeat=2)
    ]

    if search:
        for names in ResolveNames(
                account.protocol).call(unresolved_entries=(search, )):
            click.secho(f'{names}')
    else:
        atoz = [
            ''.join(x)
            for x in itertools.product(string.ascii_lowercase, repeat=2)
        ]
        for entry in atoz:
            for names in ResolveNames(
                    account.protocol).call(unresolved_entries=(entry, )):
                click.secho(f'{names}')

    click.secho(f'-------------------------------------\n', dim=True)
예제 #6
0
    def test_resolvenames_parsing(self):
        # Test static XML since server has no roomlists
        xml = b'''\
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <m:ResolveNamesResponse
            xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
            xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Warning">
          <m:MessageText>Multiple results were found.</m:MessageText>
          <m:ResponseCode>ErrorNameResolutionMultipleResults</m:ResponseCode>
          <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
          <m:ResolutionSet TotalItemsInView="2" IncludesLastItemInRange="true">
            <t:Resolution>
              <t:Mailbox>
                <t:Name>John Doe</t:Name>
                <t:EmailAddress>[email protected]</t:EmailAddress>
                <t:RoutingType>SMTP</t:RoutingType>
                <t:MailboxType>Mailbox</t:MailboxType>
              </t:Mailbox>
            </t:Resolution>
            <t:Resolution>
              <t:Mailbox>
                <t:Name>John Deer</t:Name>
                <t:EmailAddress>[email protected]</t:EmailAddress>
                <t:RoutingType>SMTP</t:RoutingType>
                <t:MailboxType>Mailbox</t:MailboxType>
              </t:Mailbox>
            </t:Resolution>
          </m:ResolutionSet>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </m:ResolveNamesResponse>
  </s:Body>
</s:Envelope>'''
        ws = ResolveNames(self.account.protocol)
        ws.return_full_contact_data = False
        self.assertSetEqual({m.email_address
                             for m in ws.parse(xml)},
                            {'*****@*****.**', '*****@*****.**'})
예제 #7
0
    def test_element_container(self):
        ws = ResolveNames(self.account.protocol)
        xml = b"""\
<?xml version="1.0" encoding="utf-8" ?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <m:ResolveNamesResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </m:ResolveNamesResponse>
  </s:Body>
</s:Envelope>"""
        with self.assertRaises(TransportError) as e:
            # Missing ResolutionSet elements
            list(ws.parse(xml))
        self.assertIn("ResolutionSet elements in ResponseMessage",
                      e.exception.args[0])
예제 #8
0
    def test_element_container(self):
        svc = ResolveNames(self.account.protocol)
        soap_xml = b"""\
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <m:ResolveNamesResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </m:ResolveNamesResponse>
  </soap:Body>
</soap:Envelope>"""
        header, body = svc._get_soap_parts(response=MockResponse(soap_xml))
        resp = svc._get_soap_messages(body=body)
        with self.assertRaises(TransportError) as e:
            # Missing ResolutionSet elements
            list(svc._get_elements_in_response(response=resp))
        self.assertIn('ResolutionSet elements in ResponseMessage', e.exception.args[0])
예제 #9
0
 def test_resolvenames(self):
     with self.assertRaises(ValueError) as e:
         self.account.protocol.resolve_names(names=[], search_scope="XXX")
     self.assertEqual(
         e.exception.args[0],
         f"'search_scope' 'XXX' must be one of {sorted(SEARCH_SCOPE_CHOICES)}"
     )
     with self.assertRaises(ValueError) as e:
         self.account.protocol.resolve_names(names=[], shape="XXX")
     self.assertEqual(
         e.exception.args[0],
         "'contact_data_shape' 'XXX' must be one of ['AllProperties', 'Default', 'IdOnly']"
     )
     with self.assertRaises(ValueError) as e:
         ResolveNames(protocol=self.account.protocol,
                      chunk_size=500).call(unresolved_entries=None)
     self.assertEqual(
         e.exception.args[0],
         "Chunk size 500 is too high. ResolveNames supports returning at most 100 candidates for a lookup",
     )
     tmp = self.account.protocol.version
     self.account.protocol.config.version = Version(EXCHANGE_2010_SP1)
     with self.assertRaises(NotImplementedError) as e:
         self.account.protocol.resolve_names(names=["*****@*****.**"],
                                             shape="IdOnly")
     self.account.protocol.config.version = tmp
     self.assertEqual(
         e.exception.args[0],
         "'contact_data_shape' is only supported for Exchange 2010 SP2 servers and later"
     )
     self.assertGreaterEqual(
         self.account.protocol.resolve_names(names=["*****@*****.**"]), [])
     self.assertGreaterEqual(
         self.account.protocol.resolve_names(
             names=["*****@*****.**"],
             search_scope="ActiveDirectoryContacts"), [])
     self.assertGreaterEqual(
         self.account.protocol.resolve_names(names=["*****@*****.**"],
                                             shape="AllProperties"), [])
     self.assertGreaterEqual(
         self.account.protocol.resolve_names(
             names=["*****@*****.**"],
             parent_folders=[self.account.contacts]), [])
     self.assertEqual(
         self.account.protocol.resolve_names(
             names=[self.account.primary_smtp_address]),
         [Mailbox(email_address=self.account.primary_smtp_address)],
     )
     # Test something that's not an email
     self.assertEqual(
         self.account.protocol.resolve_names(names=["foo\\bar"]),
         [ErrorNameResolutionNoResults("No results were found.")],
     )
     # Test return_full_contact_data
     mailbox, contact = self.account.protocol.resolve_names(
         names=[self.account.primary_smtp_address],
         return_full_contact_data=True)[0]
     self.assertEqual(
         mailbox, Mailbox(email_address=self.account.primary_smtp_address))
     self.assertListEqual(
         [
             e.email.replace("SMTP:", "")
             for e in contact.email_addresses if e.label == "EmailAddress1"
         ],
         [self.account.primary_smtp_address],
     )
예제 #10
0
    def test_soap_error(self):
        soap_xml = """\
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <t:ServerVersionInfo MajorVersion="8" MinorVersion="0" MajorBuildNumber="685" MinorBuildNumber="8"
                         xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
  </soap:Header>
  <soap:Body>
    <soap:Fault>
      <faultcode>{faultcode}</faultcode>
      <faultstring>{faultstring}</faultstring>
      <faultactor>https://CAS01.example.com/EWS/Exchange.asmx</faultactor>
      <detail>
        <ResponseCode xmlns="http://schemas.microsoft.com/exchange/services/2006/errors">{responsecode}</ResponseCode>
        <Message xmlns="http://schemas.microsoft.com/exchange/services/2006/errors">{message}</Message>
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>"""
        header, body = ResolveNames._get_soap_parts(response=MockResponse(soap_xml.format(
                faultcode='YYY', faultstring='AAA', responsecode='XXX', message='ZZZ'
            ).encode('utf-8')))
        with self.assertRaises(SOAPError) as e:
            ResolveNames._get_soap_messages(body=body)
        self.assertIn('AAA', e.exception.args[0])
        self.assertIn('YYY', e.exception.args[0])
        self.assertIn('ZZZ', e.exception.args[0])
        header, body = ResolveNames._get_soap_parts(response=MockResponse(soap_xml.format(
                faultcode='ErrorNonExistentMailbox', faultstring='AAA', responsecode='XXX', message='ZZZ'
            ).encode('utf-8')))
        with self.assertRaises(ErrorNonExistentMailbox) as e:
            ResolveNames._get_soap_messages(body=body)
        self.assertIn('AAA', e.exception.args[0])
        header, body = ResolveNames._get_soap_parts(response=MockResponse(soap_xml.format(
                faultcode='XXX', faultstring='AAA', responsecode='ErrorNonExistentMailbox', message='YYY'
            ).encode('utf-8')))
        with self.assertRaises(ErrorNonExistentMailbox) as e:
            ResolveNames._get_soap_messages(body=body)
        self.assertIn('YYY', e.exception.args[0])

        # Test bad XML (no body)
        soap_xml = b"""\
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:ServerVersionInfo MajorVersion="8" MinorVersion="0" MajorBuildNumber="685" MinorBuildNumber="8"
                         xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
  </soap:Header>
  </soap:Body>
</soap:Envelope>"""
        with self.assertRaises(MalformedResponseError):
            ResolveNames._get_soap_parts(response=MockResponse(soap_xml))

        # Test bad XML (no fault)
        soap_xml = b"""\
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:ServerVersionInfo MajorVersion="8" MinorVersion="0" MajorBuildNumber="685" MinorBuildNumber="8"
                         xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
  </soap:Header>
  <soap:Body>
    <soap:Fault>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>"""
        header, body = ResolveNames._get_soap_parts(response=MockResponse(soap_xml))
        with self.assertRaises(TransportError):
            ResolveNames._get_soap_messages(body=body)
예제 #11
0
def gal(dump, search, verbose, full, output):
    """
        Dump GAL using EWS.
        The slower technique used by https://github.com/dafthack/MailSniper
        default searches from "aa" to "zz" and prints them all.
        EWS only returns batches of 100
        There will be doubles, so uniq after.
    """

    if verbose:
        logging.basicConfig(level=logging.DEBUG, handlers=[PrettyXmlHandler()])

    credentials = Credentials(tbestate.username, tbestate.password)
    username = tbestate.username

    try:
        if tbestate.exch_host:
            config = Configuration(server=tbestate.exch_host, credentials=credentials)
            account = Account(username, config=config, autodiscover=False, access_type=DELEGATE)
        else:
            account = Account(username, credentials=credentials, autodiscover=True, access_type=DELEGATE)

    except exchangelib.errors.ErrorNonExistentMailbox as neb:
        print(f'[!] Mailbox does not exist for user: {username}')
        exit()
    except exchangelib.errors.UnauthorizedError as err:
        print(f'[!] Invalid credentials for user: {username}')
        exit()
    except exchangelib.errors.TransportError:
        print(f'[!] Can not reach target Exchange server: {tbestate.exch_host}')
        exit()
    except Exception as err:
        print(f'[!] Something went wrong: {err}')


    atoz = [''.join(x) for x in itertools.product(string.ascii_lowercase, repeat=2)]

    if search:
        for names in ResolveNames(account.protocol).call(unresolved_entries=(search,)):
            if output:
                click.secho(f'{names}')
                output.write(f'{names}\n')
            else:
                click.secho(f'{names}')

    elif full:
        atoz = [''.join(x) for x in itertools.product(string.ascii_lowercase, repeat=2)]
        for entry in atoz:
            for names in ResolveNames(account.protocol).call(unresolved_entries=(entry,)):
                stringed = str(names)
                found = re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+', stringed)
                for i in found:
                    if output:
                        click.secho(f'{i}')
                        output.write(f'{i}\n')
                    else:
                        click.secho(f'{i}')
    else:
        atoz = [''.join(x) for x in itertools.product(string.ascii_lowercase, repeat=2)]
        for entry in atoz:
            for names in ResolveNames(account.protocol).call(unresolved_entries=(entry,)):
                if output:
                    click.secho(f'{names}')
                    output.write(f'{names}\n')
                else:
                    click.secho(f'{names}')


    click.secho(f'-------------------------------------\n', dim=True)