示例#1
0
    def start(self, restart=False):
        """
        Launch the firewall using the privileged wrapper.

        :returns: True if the exitcode of calling the root helper in a
                  subprocess is 0.
        :rtype: bool
        """
        gateways = [gateway for gateway, port in self._remotes]

        # XXX check for wrapper existence, check it's root owned etc.
        # XXX check that the iptables rules are in place.

        cmd = ["pkexec", self.BITMASK_ROOT, "firewall", "start"]
        if restart:
            cmd.append("restart")

        # FIXME -- use a processprotocol
        exitCode = subprocess.call(cmd + gateways)
        emit_async(catalog.VPN_STATUS_CHANGED)

        if exitCode == 0:
            return True
        else:
            return False
示例#2
0
    def _route_msg(self, encrypt_and_sign_result, raw):
        """
        Sends the msg using the ESMTPSenderFactory.

        :param encrypt_and_sign_result: A tuple containing the 'maybe'
                                        encrypted message and the recipient
        :type encrypt_and_sign_result: tuple
        """
        message, recipient = encrypt_and_sign_result
        self.log.info(
            'Connecting to SMTP server %s:%s' % (self._host, self._port))
        msg = message.as_string(False)

        # we construct a defer to pass to the ESMTPSenderFactory
        d = defer.Deferred()
        d.addCallback(self.sendSuccess)
        d.addErrback(self.sendError, raw)
        # we don't pass an ssl context factory to the ESMTPSenderFactory
        # because ssl will be handled by reactor.connectSSL() below.
        factory = smtp.ESMTPSenderFactory(
            "",  # username is blank, no client auth here
            "",  # password is blank, no client auth here
            self._from_address,
            recipient.dest.addrstr,
            StringIO(msg),
            d,
            heloFallback=True,
            requireAuthentication=False,
            requireTransportSecurity=True)
        factory.domain = bytes('leap.bitmask.mail-' + __version__)
        emit_async(catalog.SMTP_SEND_MESSAGE_START,
                   self._from_address, recipient.dest.addrstr)
        reactor.connectSSL(
            self._host, self._port, factory,
            contextFactory=SSLContextFactory(self._cert, self._key))
示例#3
0
 def push_status(self):
     try:
         statusdict = self.do_status()
         status = statusdict.get('status').upper()
         emit_async(catalog.VPN_STATUS_CHANGED, status)
     except ValueError:
         pass
示例#4
0
 def __cb_signal_unread_to_ui(self, unseen):
     """
     Send the unread signal to UI.
     :param unseen: number of unseen messages.
     :type unseen: int
     """
     emit_async(catalog.MAIL_UNREAD_MESSAGES, str(unseen))
示例#5
0
    def start(self, restart=False):
        """
        Launch the firewall using the privileged wrapper.

        :returns: True if the exitcode of calling the root helper in a
                  subprocess is 0.
        :rtype: bool
        """
        gateways = [gateway for gateway, port in self._remotes]

        # XXX check for wrapper existence, check it's root owned etc.
        # XXX check that the iptables rules are in place.

        cmd = [self.BITMASK_ROOT, "firewall", "start"]
        cmd = check_root(cmd)

        if restart:
            cmd.append("restart")
        result = '<did not run>'
        if not os.path.isfile(self.BITMASK_ROOT):
            raise FirewallError('Could not find bitmask-root!')
        try:
            retcode, result = commands.getstatusoutput(' '.join(cmd +
                                                                gateways))
        except Exception:
            msg = 'Error launching the firewall'
            log.failure(msg)
            if NOT_ROOT:
                raise FirewallError(msg)
        finally:
            log.debug(result)
        emit_async(catalog.VPN_STATUS_CHANGED, "FW_ON")
        return True
示例#6
0
 def _set_status(self, address, status, error=None, unread=None):
     self._status[address] = {
         "status": status,
         "error": error,
         "unread": unread
     }
     emit_async(catalog.MAIL_STATUS_CHANGED, address)
示例#7
0
 def _set_status(self, address, status, error=None, keys=None):
     self._status[address] = {
         "status": status,
         "error": error,
         "keys": keys
     }
     emit_async(catalog.MAIL_STATUS_CHANGED, address)
示例#8
0
    def _route_msg(self, encrypt_and_sign_result, recipient, raw):
        """
        Sends the msg using the ESMTPSenderFactory.

        :param encrypt_and_sign_result: A tuple containing the 'maybe'
                                        encrypted message and the recipient
        :type encrypt_and_sign_result: tuple
        """
        message, recipient = encrypt_and_sign_result
        msg = message.as_string(False)

        d = None
        for sender in self._senders:
            if sender.can_send(recipient.dest.addrstr):
                self.log.debug('Sending message to %s with: %s'
                               % (recipient, str(sender)))
                d = sender.send(recipient, msg)
                break

        if d is None:
            return self.sendError(Failure(), raw)

        emit_async(catalog.SMTP_SEND_MESSAGE_START,
                   self._from_address, recipient.dest.addrstr)
        d.addCallback(self.sendSuccess)
        d.addErrback(self.sendError, raw)
        return d
示例#9
0
    def send_key(self):
        """
        Send user's key to provider.

        Public key bound to user's is sent to provider, which will
        replace any prior keys for the same address in its database.

        :return: A Deferred which fires when the key is sent, or which fails
                 with KeyNotFound if the key was not found in local database.
        :rtype: Deferred

        :raise UnsupportedKeyTypeError: if invalid key type
        """
        if not self.token:
            self.log.debug('Token not available, scheduling '
                           'a new key sending attempt...')
            yield task.deferLater(reactor, 5, self.send_key)

        self.log.info('Sending public key to server')
        key = yield self.get_key(self._address, fetch_remote=False)
        yield self._nicknym.put_key(self.uid, key.key_data, self._api_uri,
                                    self._api_version)
        emit_async(catalog.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
        self.log.info('Key sent to server')
        defer.returnValue(key)
示例#10
0
    def gen_key(self, ktype):
        """
        Generate a key of type ktype bound to the user's address.

        :param ktype: The type of the key.
        :type ktype: subclass of EncryptionKey

        :return: A Deferred which fires with the generated EncryptionKey.
        :rtype: Deferred

        :raise UnsupportedKeyTypeError: if invalid key type
        """
        self._assert_supported_key_type(ktype)
        _keys = self._wrapper_map[ktype]

        def signal_finished(key):
            emit_async(
                catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
            return key

        emit_async(catalog.KEYMANAGER_STARTED_KEY_GENERATION, self._address)

        d = _keys.gen_key(self._address)
        d.addCallback(signal_finished)
        return d
示例#11
0
 def __cb_signal_unread_to_ui(self, unseen):
     """
     Send the unread signal to UI.
     :param unseen: number of unseen messages.
     :type unseen: int
     """
     emit_async(catalog.MAIL_UNREAD_MESSAGES, self.store.uuid, str(unseen))
示例#12
0
    def _route_msg(self, encrypt_and_sign_result):
        """
        Sends the msg using the ESMTPSenderFactory.

        :param encrypt_and_sign_result: A tuple containing the 'maybe'
                                        encrypted message and the recipient
        :type encrypt_and_sign_result: tuple
        """
        message, recipient = encrypt_and_sign_result
        log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port))
        msg = message.as_string(False)

        # we construct a defer to pass to the ESMTPSenderFactory
        d = defer.Deferred()
        d.addCallbacks(self.sendSuccess, self.sendError)
        # we don't pass an ssl context factory to the ESMTPSenderFactory
        # because ssl will be handled by reactor.connectSSL() below.
        factory = smtp.ESMTPSenderFactory(
            "",  # username is blank, no client auth here
            "",  # password is blank, no client auth here
            self._from_address,
            recipient.dest.addrstr,
            StringIO(msg),
            d,
            heloFallback=True,
            requireAuthentication=False,
            requireTransportSecurity=True)
        factory.domain = __version__
        emit_async(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr)
        reactor.connectSSL(
            self._host, self._port, factory,
            contextFactory=SSLContextFactory(self._cert, self._key))
示例#13
0
    def get_key(self, address, ktype, private=False, fetch_remote=True):
        """
        Return a key of type ktype bound to address.

        First, search for the key in local storage. If it is not available,
        then try to fetch from nickserver.

        :param address: The address bound to the key.
        :type address: str
        :param ktype: The type of the key.
        :type ktype: subclass of EncryptionKey
        :param private: Look for a private key instead of a public one?
        :type private: bool
        :param fetch_remote: If key not found in local storage try to fetch
                             from nickserver
        :type fetch_remote: bool

        :return: A Deferred which fires with an EncryptionKey of type ktype
                 bound to address, or which fails with KeyNotFound if no key
                 was found neither locally or in keyserver.
        :rtype: Deferred

        :raise UnsupportedKeyTypeError: if invalid key type
        """
        self._assert_supported_key_type(ktype)
        logger.debug("getting key for %s" % (address,))
        leap_assert(
            ktype in self._wrapper_map,
            'Unkown key type: %s.' % str(ktype))
        _keys = self._wrapper_map[ktype]

        emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)

        def key_found(key):
            emit_async(catalog.KEYMANAGER_KEY_FOUND, address)
            return key

        def key_not_found(failure):
            if not failure.check(KeyNotFound):
                return failure

            emit_async(catalog.KEYMANAGER_KEY_NOT_FOUND, address)

            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                return failure

            emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
            d = self._fetch_keys_from_server(address)
            d.addCallback(
                lambda _: _keys.get_key(address, private=False))
            d.addCallback(key_found)
            return d

        # return key if it exists in local database
        d = _keys.get_key(address, private=private)
        d.addCallbacks(key_found, key_not_found)
        return d
示例#14
0
        def if_key_not_found_send_unencrypted(failure, message):
            failure.trap(KeyNotFound, KeyAddressMismatch)

            log.msg('Will send unencrypted message to %s.' % to_address)
            emit_async(catalog.SMTP_START_SIGN, self._from_address)
            d = self._sign(message, from_address)
            d.addCallback(signal_sign)
            return d
示例#15
0
 def sendSuccess(self, dest_addrstr):
     """
     Callback for a successful send.
     """
     fromaddr = self._from_address
     self.log.info('Message sent from %s to %s' % (fromaddr, dest_addrstr))
     emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS,
                fromaddr, dest_addrstr)
示例#16
0
        def if_key_not_found_send_unencrypted(failure, message):
            failure.trap(KeyNotFound, KeyAddressMismatch)

            self.log.info('Will send unencrypted message to %s.' % to_address)
            emit_async(catalog.SMTP_START_SIGN, self._from_address, to_address)
            d = self._sign(message, from_address)
            d.addCallback(signal_sign)
            return d
示例#17
0
 def __cb_signal_unread_to_ui(self, unseen):
     """
     Send the unread signal to UI.
     :param unseen: number of unseen messages.
     :type unseen: int
     """
     # TODO it might make sense to modify the event so that
     # it receives both the mailbox name AND the number of unread messages.
     emit_async(catalog.MAIL_UNREAD_MESSAGES, self.store.uuid, str(unseen))
示例#18
0
        def process_decrypted(res):
            if isinstance(res, tuple):
                decrdata, _ = res
                success = True
            else:
                decrdata = ""
                success = False

            emit_async(catalog.MAIL_MSG_DECRYPTED, "1" if success else "0")
            return self._process_decrypted_doc(doc, decrdata)
示例#19
0
    def connectionLost(self):
        """
        Log an error when the connection is lost.
        """
        log.msg("Connection lost unexpectedly!")
        log.err()
        emit_async(catalog.SMTP_CONNECTION_LOST, self._user.dest.addrstr)
        # unexpected loss of connection; don't save

        self._lines = []
示例#20
0
    def connectionLost(self):
        """
        Log an error when the connection is lost.
        """
        self.log.error('Connection lost unexpectedly!')
        emit_async(catalog.SMTP_CONNECTION_LOST, self._userid,
                   self._user.dest.addrstr)
        # unexpected loss of connection; don't save

        self._lines = []
示例#21
0
        def process_decrypted(res):
            if isinstance(res, tuple):
                decrdata, _ = res
                success = True
            else:
                decrdata = ""
                success = False

            emit_async(catalog.MAIL_MSG_DECRYPTED, self._userid,
                       "1" if success else "0")
            return self._process_decrypted_doc(doc, decrdata)
示例#22
0
        def msgSavedCallback(result):

            def signal_deleted(doc_id):
                emit_async(catalog.MAIL_MSG_DELETED_INCOMING,
                           self._userid)
                return doc_id

            emit_async(catalog.MAIL_MSG_SAVED_LOCALLY, self._userid)
            d = self._delete_incoming_message(doc)
            d.addCallback(signal_deleted)
            return d
示例#23
0
def run_service(store, **kwargs):
    """
    Main entry point to run the service from the client.

    :param store: a soledad instance

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    """
    leap_check(store, "store cannot be None")
    # XXX this can also be a ProxiedObject, FIXME
    # leap_assert_type(store, Soledad)

    port = kwargs.get('port', IMAP_PORT)
    userid = kwargs.get('userid', None)
    leap_check(userid is not None, "need an user id")

    uuid = store.uuid
    factory = LeapIMAPFactory(uuid, userid, store)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access imap from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        tport = reactor.listenTCP(port, factory,
                                  interface=interface)
    except CannotListenError:
        logger.error("IMAP Service failed to start: "
                     "cannot listen in port %s" % (port,))
    except Exception as exc:
        logger.error("Error launching IMAP service: %r" % (exc,))
    else:
        # all good.

        if DO_MANHOLE:
            # TODO get pass from env var.too.
            manhole_factory = manhole.getManholeFactory(
                {'f': factory,
                 'a': factory.theAccount,
                 'gm': factory.theAccount.getMailbox},
                "boss", "leap")
            reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory,
                              interface="127.0.0.1")
        logger.debug("IMAP4 Server is RUNNING in port  %s" % (port,))
        emit_async(catalog.IMAP_SERVICE_STARTED, str(port))

        # FIXME -- change service signature
        return tport, factory

    # not ok, signal error.
    emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
示例#24
0
    def sendSuccess(self, smtp_sender_result):
        """
        Callback for a successful send.

        :param smtp_sender_result: The result from the ESMTPSender from
                                   _route_msg
        :type smtp_sender_result: tuple(int, list(tuple))
        """
        dest_addrstr = smtp_sender_result[1][0][0]
        log.msg('Message sent to %s' % dest_addrstr)
        emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, dest_addrstr)
示例#25
0
    def sendSuccess(self, smtp_sender_result):
        """
        Callback for a successful send.

        :param smtp_sender_result: The result from the ESMTPSender from
                                   _route_msg
        :type smtp_sender_result: tuple(int, list(tuple))
        """
        dest_addrstr = smtp_sender_result[1][0][0]
        fromaddr = self._from_address
        log.msg('Message sent from %s to %s' % (fromaddr, dest_addrstr))
        emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, fromaddr, dest_addrstr)
示例#26
0
    def get_key(self, address, private=False, fetch_remote=True):
        """
        Return a key bound to address.

        First, search for the key in local storage. If it is not available,
        then try to fetch from nickserver.

        :param address: The address bound to the key.
        :type address: str
        :param private: Look for a private key instead of a public one?
        :type private: bool
        :param fetch_remote: If key not found in local storage try to fetch
                             from nickserver
        :type fetch_remote: bool

        :return: A Deferred which fires with an EncryptionKey bound to address,
                 or which fails with KeyNotFound if no key was found neither
                 locally or in keyserver or fail with KeyVersionError if the
                 key has a format not supported by this version of KeyManager
        :rtype: Deferred

        :raise UnsupportedKeyTypeError: if invalid key type
        """
        logger.debug("getting key for %s" % (address, ))

        emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)

        def key_found(key):
            emit_async(catalog.KEYMANAGER_KEY_FOUND, address)
            return key

        def key_not_found(failure):
            if not failure.check(KeyNotFound):
                return failure

            emit_async(catalog.KEYMANAGER_KEY_NOT_FOUND, address)

            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                return failure

            emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
            d = self._fetch_keys_from_server(address)
            d.addCallback(
                lambda _: self._openpgp.get_key(address, private=False))
            d.addCallback(key_found)
            return d

        # return key if it exists in local database
        d = self._openpgp.get_key(address, private=private)
        d.addCallbacks(key_found, key_not_found)
        return d
示例#27
0
        def not_found(failure):
            failure.trap(KeyNotFound)

            # if key was not found, check config to see if will send anyway
            if self._encrypted_only:
                emit_async(catalog.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
                raise smtp.SMTPBadRcpt(user.dest.addrstr)
            log.msg("Warning: will send an unencrypted message (because "
                    "encrypted_only' is set to False).")
            emit_async(
                catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED,
                user.dest.addrstr)
示例#28
0
    def set_status(self, status, errcode):
        if status in ("AUTH", "WAIT", "CONNECTING", "GET_CONFIG",
                      "ASSIGN_IP", "ADD_ROUTES", "RECONNECTING"):
            status = "starting"
        elif status == "EXITING":
            status = "stopping"
        elif status == "CONNECTED":
            status = "on"

        self._status = status
        self.errcode = errcode
        emit_async(catalog.VPN_STATUS_CHANGED)
示例#29
0
 def stop(self):
     """
     Tear the firewall down using the privileged wrapper.
     """
     cmd = [self.BITMASK_ROOT, "firewall", "stop"]
     cmd = check_root(cmd)
     exitCode = subprocess.call(cmd)
     emit_async(catalog.VPN_STATUS_CHANGED, "FW_OFF")
     if exitCode == 0:
         return True
     else:
         return False
示例#30
0
        def not_found(failure):
            failure.trap(KeyNotFound)

            # if key was not found, check config to see if will send anyway
            if self._encrypted_only:
                emit_async(catalog.SMTP_RECIPIENT_REJECTED, self._userid,
                           user.dest.addrstr)
                raise smtp.SMTPBadRcpt(user.dest.addrstr)
            self.log.warn('Warning: will send an unencrypted message (because '
                          '"encrypted_only" is set to False).')
            emit_async(catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED,
                       self._userid, user.dest.addrstr)
示例#31
0
        def _fetch_remotely(passthru):
            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                return passthru

            self.log.debug('Fetching remotely key for %s.' % address)
            emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
            d = self._fetch_keys_from_server_and_store_local(address)
            d.addCallback(
                lambda _: self._openpgp.get_key(address, private=False))
            d.addCallback(key_found)
            return d
示例#32
0
def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
                       smtp_cert, smtp_key, encrypted_only):
    """
    Setup SMTP gateway to run with Twisted.

    This function sets up the SMTP gateway configuration and the Twisted
    reactor.

    :param port: The port in which to run the server.
    :type port: int
    :param userid: The user currently logged in
    :type userid: str
    :param keymanager: A Key Manager from where to get recipients' public
                       keys.
    :type keymanager: leap.common.keymanager.KeyManager
    :param smtp_host: The hostname of the remote SMTP server.
    :type smtp_host: str
    :param smtp_port: The port of the remote SMTP server.
    :type smtp_port: int
    :param smtp_cert: The client certificate for authentication.
    :type smtp_cert: str
    :param smtp_key: The client key for authentication.
    :type smtp_key: str
    :param encrypted_only: Whether the SMTP gateway should send unencrypted
                           mail or not.
    :type encrypted_only: bool

    :returns: tuple of SMTPFactory, twisted.internet.tcp.Port
    """
    # configure the use of this service with twistd
    outgoing_mail = OutgoingMail(
        userid, keymanager, smtp_cert, smtp_key, smtp_host, smtp_port)
    factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail)
    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access smtp from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        tport = reactor.listenTCP(port, factory, interface=interface)
        emit_async(catalog.SMTP_SERVICE_STARTED, str(port))
        return factory, tport
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % port)
        emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
    except Exception as exc:
        logger.error("Unhandled error while launching smtp gateway service")
        logger.exception(exc)
示例#33
0
def run_service(soledad_sessions, port=IMAP_PORT):
    """
    Main entry point to run the service from the client.

    :param soledad_sessions: a dict-like object, containing instances
                             of a Store (soledad instances), indexed by userid.

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    :rtype: tuple
    """
    factory = LeapIMAPFactory(soledad_sessions)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access imap from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        # TODO use Endpoints !!!
        tport = reactor.listenTCP(port, factory, interface=interface)
    except CannotListenError:
        logger.error("IMAP Service failed to start: "
                     "cannot listen in port %s" % (port, ))
    except Exception as exc:
        logger.error("Error launching IMAP service: %r" % (exc, ))
    else:
        # all good.

        if DO_MANHOLE:
            # TODO get pass from env var.too.
            manhole_factory = manhole.getManholeFactory(
                {
                    'f': factory,
                    'gm': factory.theAccount.getMailbox
                }, "boss", "leap")
            # TODO  use Endpoints !!!
            reactor.listenTCP(manhole.MANHOLE_PORT,
                              manhole_factory,
                              interface="127.0.0.1")
        logger.debug("IMAP4 Server is RUNNING in port  %s" % (port, ))
        emit_async(catalog.IMAP_SERVICE_STARTED, str(port))

        # FIXME -- change service signature
        return tport, factory

    # not ok, signal error.
    emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
示例#34
0
        def msgSavedCallback(result):
            if empty(result):
                return

            for listener in self._listeners:
                listener(result)

            def signal_deleted(doc_id):
                emit_async(catalog.MAIL_MSG_DELETED_INCOMING)
                return doc_id

            emit_async(catalog.MAIL_MSG_SAVED_LOCALLY)
            d = self._delete_incoming_message(doc)
            d.addCallback(signal_deleted)
            return d
示例#35
0
    def stop(self):
        """
        Tear the firewall down using the privileged wrapper.
        """
        if IS_MAC:
            # We don't support Mac so far
            return True

        exitCode = subprocess.call(
            ["pkexec", self.BITMASK_ROOT, "firewall", "stop"])
        emit_async(catalog.VPN_STATUS_CHANGED)
        if exitCode == 0:
            return True
        else:
            return False
示例#36
0
def run_service(soledad_sessions, port=IMAP_PORT):
    """
    Main entry point to run the service from the client.

    :param soledad_sessions: a dict-like object, containing instances
                             of a Store (soledad instances), indexed by userid.

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    :rtype: tuple
    """
    factory = LeapIMAPFactory(soledad_sessions)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access imap from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        # TODO use Endpoints !!!
        tport = reactor.listenTCP(port, factory,
                                  interface=interface)
    except CannotListenError:
        logger.error("IMAP Service failed to start: "
                     "cannot listen in port %s" % (port,))
    except Exception as exc:
        logger.error("Error launching IMAP service: %r" % (exc,))
    else:
        # all good.

        if DO_MANHOLE:
            # TODO get pass from env var.too.
            manhole_factory = manhole.getManholeFactory(
                {'f': factory,
                 'gm': factory.theAccount.getMailbox},
                "boss", "leap")
            # TODO  use Endpoints !!!
            reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory,
                              interface="127.0.0.1")
        logger.debug("IMAP4 Server is RUNNING in port  %s" % (port,))
        emit_async(catalog.IMAP_SERVICE_STARTED, str(port))

        # FIXME -- change service signature
        return tport, factory

    # not ok, signal error.
    emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
示例#37
0
    def _process_incoming_mail(self, doclist):
        """
        Iterates through the doclist, checks if each doc
        looks like a message, and yields a deferred that will decrypt and
        process the message.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: a list of deferreds for individual messages.
        """
        self.log.info('Processing incoming mail')
        if not doclist:
            self.log.debug("no incoming messages found")
            return
        num_mails = len(doclist)

        deferreds = []
        for index, doc in enumerate(doclist):
            self.log.debug(
                'Processing Incoming Message: %d of %d'
                % (index + 1, num_mails))
            emit_async(catalog.MAIL_MSG_PROCESSING, self._userid,
                       str(index), str(num_mails))

            keys = doc.content.keys()

            # TODO Compatibility check with the index in pre-0.6 mx
            # that does not write the ERROR_DECRYPTING_KEY
            # This should be removed in 0.7
            # TODO deprecate this already

            has_errors = doc.content.get(fields.ERROR_DECRYPTING_KEY, None)
            if has_errors is None:
                warnings.warn('JUST_MAIL_COMPAT_IDX will be deprecated!',
                              DeprecationWarning)

            if has_errors:
                self.log.debug('Skipping message with decrypting errors...')
            elif self._is_msg(keys):
                # TODO this pipeline is a bit obscure!
                d = self._decrypt_doc(doc)
                d.addCallbacks(self._add_message_locally, self._errback)
                deferreds.append(d)

        d = defer.gatherResults(deferreds, consumeErrors=True)
        d.addCallback(lambda _: doclist)
        return d
示例#38
0
    def _signal_fetch_to_ui(self, doclist):
        """
        Send leap events to ui.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: doclist
        :rtype: iterable
        """
        if doclist:
            fetched_ts = time.mktime(time.gmtime())
            num_mails = len(doclist) if doclist is not None else 0
            if num_mails != 0:
                log.msg("there are %s mails" % (num_mails,))
            emit_async(catalog.MAIL_FETCHED_INCOMING, self._userid,
                       str(num_mails), str(fetched_ts))
            return doclist
示例#39
0
        def key_not_found(failure):
            if not failure.check(KeyNotFound):
                return failure

            emit_async(catalog.KEYMANAGER_KEY_NOT_FOUND, address)

            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                return failure

            emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
            d = self._fetch_keys_from_server(address)
            d.addCallback(
                lambda _: _keys.get_key(address, private=False))
            d.addCallback(key_found)
            return d
示例#40
0
 def send(pubkey):
     data = {self.PUBKEY_KEY: pubkey.key_data}
     uri = "%s/%s/users/%s.json" % (self._api_uri, self._api_version,
                                    self._uid)
     d = self._put(uri, data)
     d.addCallback(lambda _: emit_async(
         catalog.KEYMANAGER_DONE_UPLOADING_KEYS, self._address))
     return d
示例#41
0
        def key_not_found(failure):
            if not failure.check(keymanager_errors.KeyNotFound):
                return failure

            emit_async(catalog.KEYMANAGER_KEY_NOT_FOUND, address)

            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                return failure

            emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
            d = self._fetch_keys_from_server_and_store_local(address)
            d.addCallback(
                lambda _: self._openpgp.get_key(address, private=False))
            d.addCallback(key_found)
            return d
示例#42
0
    def _signal_fetch_to_ui(self, doclist):
        """
        Send leap events to ui.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: doclist
        :rtype: iterable
        """
        if doclist:
            fetched_ts = time.mktime(time.gmtime())
            num_mails = len(doclist) if doclist is not None else 0
            if num_mails != 0:
                self.log.info('There are {0!s} mails'.format(num_mails))
            emit_async(catalog.MAIL_FETCHED_INCOMING, self._userid,
                       str(num_mails), str(fetched_ts))
            return doclist
示例#43
0
    def _process_doclist(self, doclist):
        """
        Iterates through the doclist, checks if each doc
        looks like a message, and yields a deferred that will decrypt and
        process the message.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: a list of deferreds for individual messages.
        """
        log.msg('processing doclist')
        if not doclist:
            logger.debug("no docs found")
            return
        num_mails = len(doclist)

        deferreds = []
        for index, doc in enumerate(doclist):
            logger.debug("processing doc %d of %d" % (index + 1, num_mails))
            emit_async(catalog.MAIL_MSG_PROCESSING, self._userid,
                       str(index), str(num_mails))

            keys = doc.content.keys()

            # TODO Compatibility check with the index in pre-0.6 mx
            # that does not write the ERROR_DECRYPTING_KEY
            # This should be removed in 0.7

            has_errors = doc.content.get(fields.ERROR_DECRYPTING_KEY, None)
            if has_errors is None:
                warnings.warn("JUST_MAIL_COMPAT_IDX will be deprecated!",
                              DeprecationWarning)

            if has_errors:
                logger.debug("skipping msg with decrypting errors...")
            elif self._is_msg(keys):
                d = self._decrypt_doc(doc)
                d.addCallback(self._maybe_extract_keys)
                d.addCallbacks(self._add_message_locally, self._errback)
                deferreds.append(d)
        d = defer.gatherResults(deferreds, consumeErrors=True)
        d.addCallback(lambda _: doclist)
        return d
示例#44
0
def run_service(soledad_sessions,
                keymanager_sessions,
                sendmail_opts,
                port=SMTP_PORT,
                factory=None):
    """
    Main entry point to run the service from the client.

    :param soledad_sessions: a dict-like object, containing instances
                             of a Store (soledad instances), indexed by userid.
    :param keymanager_sessions: a dict-like object, containing instances
                                of Keymanager, indexed by userid.
    :param sendmail_opts: a dict-like object of sendmailOptions.
    :param factory: a factory for the protocol that will listen in the given
                    port

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    :rtype: tuple
    """
    if not factory:
        factory = SMTPFactory(soledad_sessions, keymanager_sessions,
                              sendmail_opts)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access smtp from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        # TODO Use Endpoints instead --------------------------------
        tport = reactor.listenTCP(port, factory, interface=interface)
        emit_async(catalog.SMTP_SERVICE_STARTED, str(port))

        return tport, factory
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % port)
        emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
    except Exception as exc:
        logger.error("Unhandled error while launching smtp gateway service")
        logger.error('%r' % exc)
示例#45
0
    def gen_key(self):
        """
        Generate a key bound to the user's address.

        :return: A Deferred which fires with the generated EncryptionKey.
        :rtype: Deferred

        :raise UnsupportedKeyTypeError: if invalid key type
        """
        def signal_finished(key):
            emit_async(catalog.KEYMANAGER_FINISHED_KEY_GENERATION,
                       self._address)
            return key

        emit_async(catalog.KEYMANAGER_STARTED_KEY_GENERATION, self._address)

        d = self._openpgp.gen_key(self._address)
        d.addCallback(signal_finished)
        return d
示例#46
0
 def send(pubkey):
     data = {
         self.PUBKEY_KEY: pubkey.key_data
     }
     uri = "%s/%s/users/%s.json" % (
         self._api_uri,
         self._api_version,
         self._uid)
     d = self._put(uri, data)
     d.addCallback(lambda _:
                   emit_async(catalog.KEYMANAGER_DONE_UPLOADING_KEYS,
                              self._address))
     return d
示例#47
0
def run_service(soledad_sessions, port=IMAP_PORT, factory=None):
    """
    Main entry point to run the service from the client.

    :param soledad_sessions: a dict-like object, containing instances
                             of a Store (soledad instances), indexed by userid.

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    :rtype: tuple
    """
    if not factory:
        factory = LeapIMAPFactory(soledad_sessions)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access imap from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        # TODO use Endpoints !!!
        tport = reactor.listenTCP(port, factory, interface=interface)
    except CannotListenError:
        log.error('IMAP Service failed to start: '
                  'cannot listen in port %s' % (port, ))
    except Exception as exc:
        log.error("Error launching IMAP service: %r" % (exc, ))
    else:
        # all good.
        log.debug('IMAP4 Server is RUNNING in port  %s' % (port, ))
        emit_async(catalog.IMAP_SERVICE_STARTED, str(port))

        # FIXME -- change service signature
        return tport, factory

    # not ok, signal error.
    emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
示例#48
0
def run_service(soledad_sessions, keymanager_sessions, sendmail_opts,
                port=SMTP_PORT):
    """
    Main entry point to run the service from the client.

    :param soledad_sessions: a dict-like object, containing instances
                             of a Store (soledad instances), indexed by userid.
    :param keymanager_sessions: a dict-like object, containing instances
                                of Keymanager, indexed by userid.
    :param sendmail_opts: a dict-like object of sendmailOptions.

    :returns: the port as returned by the reactor when starts listening, and
              the factory for the protocol.
    :rtype: tuple
    """
    factory = SMTPFactory(soledad_sessions, keymanager_sessions,
                          sendmail_opts)

    try:
        interface = "localhost"
        # don't bind just to localhost if we are running on docker since we
        # won't be able to access smtp from the host
        if os.environ.get("LEAP_DOCKERIZED"):
            interface = ''

        # TODO Use Endpoints instead --------------------------------
        tport = reactor.listenTCP(port, factory, interface=interface)
        emit_async(catalog.SMTP_SERVICE_STARTED, str(port))

        return factory, tport
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % port)
        emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
    except Exception as exc:
        logger.error("Unhandled error while launching smtp gateway service")
        logger.exception(exc)
示例#49
0
 def signal_encrypt_sign(newmsg):
     emit_async(catalog.SMTP_END_ENCRYPT_AND_SIGN,
                "%s,%s" % (self._from_address, to_address))
     return newmsg, recipient
示例#50
0
 def key_found(key):
     emit_async(catalog.KEYMANAGER_KEY_FOUND, address)
     return key
示例#51
0
 def found(_):
     log.msg("Accepting mail for %s..." % user.dest.addrstr)
     emit_async(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED,
                self._userid, user.dest.addrstr)
示例#52
0
    def _maybe_encrypt_and_sign(self, raw, recipient):
        """
        Attempt to encrypt and sign the outgoing message.

        The behaviour of this method depends on:

            1. the original message's content-type, and
            2. the availability of the recipient's public key.

        If the original message's content-type is "multipart/encrypted", then
        the original message is not altered. For any other content-type, the
        method attempts to fetch the recipient's public key. If the
        recipient's public key is available, the message is encrypted and
        signed; otherwise it is only signed.

        Note that, if the C{encrypted_only} configuration is set to True and
        the recipient's public key is not available, then the recipient
        address would have been rejected in SMTPDelivery.validateTo().

        The following table summarizes the overall behaviour of the gateway:

        +---------------------------------------------------+----------------+
        | content-type        | rcpt pubkey | enforce encr. | action         |
        +---------------------+-------------+---------------+----------------+
        | multipart/encrypted | any         | any           | pass           |
        | other               | available   | any           | encrypt + sign |
        | other               | unavailable | yes           | reject         |
        | other               | unavailable | no            | sign           |
        +---------------------+-------------+---------------+----------------+

        :param raw: The raw message
        :type raw: str
        :param recipient: The recipient for the message
        :type: recipient: smtp.User

        :return: A Deferred that will be fired with a MIMEMultipart message
                 and the original recipient Message
        :rtype: Deferred
        """
        # pass if the original message's content-type is "multipart/encrypted"
        origmsg = Parser().parsestr(raw)

        if origmsg.get_content_type() == 'multipart/encrypted':
            return defer.success((origmsg, recipient))

        from_address = validate_address(self._from_address)
        username, domain = from_address.split('@')
        to_address = validate_address(recipient.dest.addrstr)

        def maybe_encrypt_and_sign(message):
            d = self._encrypt_and_sign(message, to_address, from_address)
            d.addCallbacks(signal_encrypt_sign,
                           if_key_not_found_send_unencrypted,
                           errbackArgs=(message,))
            return d

        def signal_encrypt_sign(newmsg):
            emit_async(catalog.SMTP_END_ENCRYPT_AND_SIGN,
                       "%s,%s" % (self._from_address, to_address))
            return newmsg, recipient

        def if_key_not_found_send_unencrypted(failure, message):
            failure.trap(KeyNotFound, KeyAddressMismatch)

            log.msg('Will send unencrypted message to %s.' % to_address)
            emit_async(catalog.SMTP_START_SIGN, self._from_address)
            d = self._sign(message, from_address)
            d.addCallback(signal_sign)
            return d

        def signal_sign(newmsg):
            emit_async(catalog.SMTP_END_SIGN, self._from_address)
            return newmsg, recipient

        log.msg("Will encrypt the message with %s and sign with %s."
                % (to_address, from_address))
        emit_async(catalog.SMTP_START_ENCRYPT_AND_SIGN,
                   "%s,%s" % (self._from_address, to_address))
        d = self._maybe_attach_key(origmsg, from_address, to_address)
        d.addCallback(maybe_encrypt_and_sign)
        return d
示例#53
0
 def _signal_invalid_auth(failure):
     failure.trap(InvalidAuthTokenError)
     # if the token is invalid, send an event so the GUI can
     # disable mail and show an error message.
     emit_async(catalog.SOLEDAD_INVALID_AUTH_TOKEN, self._userid)
示例#54
0
 def signal_sign(newmsg):
     emit_async(catalog.SMTP_END_SIGN, self._from_address)
     return newmsg, recipient
示例#55
0
 def _signal_unread_to_ui(self, *args):
     """
     Sends unread event to ui.
     """
     emit_async(catalog.MAIL_UNREAD_MESSAGES, self._userid,
                str(self._inbox_collection.count_unseen()))
示例#56
0
 def signal_deleted(doc_id):
     emit_async(catalog.MAIL_MSG_DELETED_INCOMING,
                self._userid)
     return doc_id
示例#57
0
 def signal_finished(key):
     emit_async(
         catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
     return key