Ejemplo n.º 1
0
 def verifyHostKey(self, hostKey, fingerprint):
     host = self.transport.getPeer().host
     rv = self._isInKnownHosts(host, hostKey)
     if rv == 1:  # valid key
         return defer.succeed(1)
     elif rv == 2:  # key changed
         return defer.fail(
             ConchError("WARNING: %s' host key changed! Aborting!" % host))
     elif rv == 3:
         return defer.fail(
             ConchError("Oppening ~/.ssh/known_hosts failed." % host))
     else:
         return defer.fail(
             ConchError("WARNING: %s's host key unknown. Aborting!" % host))
Ejemplo n.º 2
0
 def openShell(self, proto):
     from twisted.internet import reactor
     if not self.ptyTuple:  # we didn't get a pty-req
         log.msg('tried to get shell without pty, failing')
         raise ConchError("no pty")
     uid, gid = self.avatar.getUserGroupId()
     homeDir = self.avatar.getHomeDir()
     shell = self.avatar.getShell()
     self.environ['USER'] = self.avatar.username
     self.environ['HOME'] = homeDir
     self.environ['SHELL'] = shell
     shellExec = os.path.basename(shell)
     peer = self.avatar.conn.transport.transport.getPeer()
     host = self.avatar.conn.transport.transport.getHost()
     self.environ['SSH_CLIENT'] = '%s %s %s' % (peer.host, peer.port,
                                                host.port)
     self.getPtyOwnership()
     self.pty = reactor.spawnProcess(proto, \
               shell, ['-%s' % shellExec], self.environ, homeDir, uid, gid,
                usePTY = self.ptyTuple)
     self.addUTMPEntry()
     fcntl.ioctl(self.pty.fileno(), tty.TIOCSWINSZ,
                 struct.pack('4H', *self.winSize))
     if self.modes:
         self.setModes()
     self.oldWrite = proto.transport.write
     proto.transport.write = self._writeHack
     self.avatar.conn.transport.transport.setTcpNoDelay(1)
Ejemplo n.º 3
0
 def testOpen(self):
     channel = SshClient.CommandChannel("foo")
     channel.openFailed(ConchError("quux", 22))
     self.assertEqual(
         "None CommandChannel Open of foo failed (error code 22): quux",
         SshClient.log.message,
     )
Ejemplo n.º 4
0
 def getPrivateKey(self):
     """
     Try to load the private key from the last used file identified by
     C{getPublicKey}, potentially asking for the passphrase if the key is
     encrypted.
     """
     file = os.path.expanduser(self.usedFiles[-1])
     if not os.path.exists(file):
         return None
     try:
         return defer.succeed(keys.Key.fromFile(file))
     except keys.EncryptedKeyError:
         for i in range(3):
             prompt = "Enter passphrase for key '%s': " % self.usedFiles[-1]
             try:
                 p = self._getPassword(prompt).encode(
                     sys.getfilesystemencoding())
                 return defer.succeed(keys.Key.fromFile(file, passphrase=p))
             except (keys.BadKeyError, ConchError):
                 pass
             return defer.fail(ConchError('bad password'))
         raise
     except KeyboardInterrupt:
         print()
         reactor.stop()
Ejemplo n.º 5
0
 def openShell(self, proto):
     if not self.ptyTuple:  # We didn't get a pty-req.
         self._log.error("tried to get shell without pty, failing")
         raise ConchError("no pty")
     uid, gid = self.avatar.getUserGroupId()
     homeDir = self.avatar.getHomeDir()
     shell = self.avatar.getShell()
     self.environ["USER"] = self.avatar.username
     self.environ["HOME"] = homeDir
     self.environ["SHELL"] = shell
     shellExec = os.path.basename(shell)
     peer = self.avatar.conn.transport.transport.getPeer()
     host = self.avatar.conn.transport.transport.getHost()
     self.environ["SSH_CLIENT"] = "%s %s %s" % (peer.host, peer.port,
                                                host.port)
     self.getPtyOwnership()
     self.pty = self._reactor.spawnProcess(
         proto,
         shell,
         ["-%s" % (shellExec, )],
         self.environ,
         homeDir,
         uid,
         gid,
         usePTY=self.ptyTuple,
     )
     self.addUTMPEntry()
     fcntl.ioctl(self.pty.fileno(), tty.TIOCSWINSZ,
                 struct.pack("4H", *self.winSize))
     if self.modes:
         self.setModes()
     self.oldWrite = proto.transport.write
     proto.transport.write = self._writeHack
     self.avatar.conn.transport.transport.setTcpNoDelay(1)
Ejemplo n.º 6
0
 def lookupChannel(self, channelType, windowSize, maxPacket, data):
     klass = self.channelLookup.get(channelType, None)
     if not klass:
         raise ConchError(OPEN_UNKNOWN_CHANNEL_TYPE, "unknown channel")
     else:
         return klass(remoteWindow=windowSize,
                      remoteMaxPacket=maxPacket,
                      data=data, avatar=self)
Ejemplo n.º 7
0
 def msg_sendRequest(self, lst):
     cn, requestType, data, wantReply = lst
     if not self.haveChannel(cn):
         if wantReply:
             self.returnDeferredWire(defer.fail(ConchError("no channel")))
     channel = self.getChannel(cn)
     d = self.conn.sendRequest(channel, requestType, data, wantReply)
     if wantReply:
         self.returnDeferredWire(d)
Ejemplo n.º 8
0
def connect(host, port, options, verifyHostKey, userAuthObject):
    if options['nocache']:
        return defer.fail(ConchError('not using connection caching'))
    d = defer.Deferred()
    filename = os.path.expanduser("~/.conch-%s-%s-%i" %
                                  (userAuthObject.user, host, port))
    factory = SSHUnixClientFactory(d, options, userAuthObject)
    reactor.connectUNIX(filename, factory, timeout=2, checkPID=1)
    return d
Ejemplo n.º 9
0
def verifyHostKey(transport, host, pubKey, fingerprint):
    goodKey = isInKnownHosts(host, pubKey, transport.factory.options)
    if goodKey == 1:  # good key
        return defer.succeed(1)
    elif goodKey == 2:  # AAHHHHH changed
        return defer.fail(ConchError('changed host key'))
    else:
        oldout, oldin = sys.stdout, sys.stdin
        sys.stdin = sys.stdout = open('/dev/tty', 'r+')
        if host == transport.transport.getPeer().host:
            khHost = host
        else:
            host = '%s (%s)' % (host, transport.transport.getPeer().host)
            khHost = '%s,%s' % (host, transport.transport.getPeer().host)
        keyType = common.getNS(pubKey)[0]
        print """The authenticity of host '%s' can't be established.
%s key fingerprint is %s.""" % (host, {
            'ssh-dss': 'DSA',
            'ssh-rsa': 'RSA'
        }[keyType], fingerprint)
        try:
            ans = raw_input(
                'Are you sure you want to continue connecting (yes/no)? ')
        except KeyboardInterrupt:
            return defer.fail(ConchError("^C"))
        while ans.lower() not in ('yes', 'no'):
            ans = raw_input("Please type 'yes' or 'no': ")
        sys.stdout, sys.stdin = oldout, oldin
        if ans == 'no':
            print 'Host key verification failed.'
            return defer.fail(ConchError('bad host key'))
        print "Warning: Permanently added '%s' (%s) to the list of known hosts." % (
            khHost, {
                'ssh-dss': 'DSA',
                'ssh-rsa': 'RSA'
            }[keyType])
        known_hosts = open(os.path.expanduser('~/.ssh/known_hosts'), 'r+')
        known_hosts.seek(-1, 2)
        if known_hosts.read(1) != '\n':
            known_hosts.write('\n')
        encodedKey = base64.encodestring(pubKey).replace('\n', '')
        known_hosts.write('%s %s %s\n' % (khHost, keyType, encodedKey))
        known_hosts.close()
        return defer.succeed(1)
Ejemplo n.º 10
0
 def _getPassword(self, prompt):
     try:
         oldout, oldin = sys.stdout, sys.stdin
         sys.stdin = sys.stdout = open('/dev/tty', 'r+')
         p = getpass.getpass(prompt)
         sys.stdout, sys.stdin = oldout, oldin
         return p
     except (KeyboardInterrupt, IOError):
         print
         raise ConchError('PEBKAC')
Ejemplo n.º 11
0
 def channelClosed(self, channel):
     # Work around a bug in Conch 0.8 - channelClosed tries to close
     # channels which are not really open yet. This is fixed since twisted's
     # SVN revision 20700.
     # FIXME: Remove after upgrade to newer twisted.
     if channel in self.channelsToRemoteChannel:
         connection.SSHConnection.channelClosed(self, channel)
     else:
         # See http://twistedmatrix.com/trac/ticket/2782.
         channel.openFailed(ConchError("Service stopped"))
Ejemplo n.º 12
0
def _verify_host_key(transport, host, pubKey, fingerprint):
    expected = transport.factory.options.get("fingerprint", None)
    if fingerprint == expected:
        return succeed(1)

    log.error(
        "SSH Host Key fingerprint of ({fp}) does not match the expected value of ({expected}).",
        fp=fingerprint,
        expected=expected)

    return fail(ConchError("Host fingerprint is unexpected."))
Ejemplo n.º 13
0
    def processEnded(self, reason):
        """
        Called when the process has ended.

        @param reason: a Failure giving the reason for the process' end.
        """
        if reason.value.exitCode != 0:
            self._getDeferred().errback(
                ConchError("exit code was not 0: %s" % reason.value.exitCode))
        else:
            buf = self.buf.replace('\r\n', '\n')
            self._getDeferred().callback(buf)
Ejemplo n.º 14
0
 def lookupChannel(self, channelType, windowSize, maxPacket, data):
     """
     Override this to get more info on the unknown channel
     """
     klass = self.channelLookup.get(channelType, None)
     if not klass:
         raise ConchError(OPEN_UNKNOWN_CHANNEL_TYPE,
                          f"unknown channel: {channelType}")
     else:
         return klass(remoteWindow=windowSize,
                      remoteMaxPacket=maxPacket,
                      data=data,
                      avatar=self)
Ejemplo n.º 15
0
 def _cbRequestIdentities(self, data):
     if ord(data[0]) != AGENT_IDENTITIES_ANSWER:
         return ConchError('unexpected respone: %i' % ord(data[0]))
     numKeys = struct.unpack('!L', data[1:5])[0]
     keys = []
     data = data[5:]
     for i in range(numKeys):
         blobLen = struct.unpack('!L', data[:4])[0]
         blob, data = data[4:4+blobLen], data[4+blobLen:]
         commLen = struct.unpack('!L', data[:4])[0]
         comm, data = data[4:4+commLen], data[4+commLen:]
         keys.append((blob, comm))
     return keys
Ejemplo n.º 16
0
 def channel_forwarded_tcpip(self, windowSize, maxPacket, data):
     log.msg('%s %s' % ('FTCP', repr(data)))
     remoteHP, origHP = forwarding.unpackOpen_forwarded_tcpip(data)
     log.msg(self.remoteForwards)
     log.msg(remoteHP)
     if remoteHP[1] in self.remoteForwards:
         connectHP = self.remoteForwards[remoteHP[1]]
         log.msg('connect forwarding %s' % (connectHP,))
         return SSHConnectForwardingChannel(connectHP,
                                         remoteWindow = windowSize,
                                         remoteMaxPacket = maxPacket,
                                         conn = self)
     else:
         raise ConchError(connection.OPEN_CONNECT_FAILED, "don't know about that port")
Ejemplo n.º 17
0
 def dataReceived(self, data):
     self.buf += data
     while 1:
         if len(self.buf) <= 4: return
         packLen = struct.unpack('!L', self.buf[:4])[0]
         if len(self.buf) < 4+packLen: return
         packet, self.buf = self.buf[4:4+packLen], self.buf[4+packLen:]
         reqType = ord(packet[0])
         d = self.deferreds.pop(0)
         if reqType == AGENT_FAILURE:
             d.errback(ConchError('agent failure'))
         elif reqType == AGENT_SUCCESS:
             d.callback('')
         else:
             d.callback(packet)
Ejemplo n.º 18
0
 def _cbRequestIdentities(self, data):
     """
     Unpack a collection of identities into a list of tuples comprised of
     public key blobs and comments.
     """
     if ord(data[0]) != AGENT_IDENTITIES_ANSWER:
         raise ConchError('unexpected response: %i' % ord(data[0]))
     numKeys = struct.unpack('!L', data[1:5])[0]
     keys = []
     data = data[5:]
     for i in range(numKeys):
         blob, data = getNS(data)
         comment, data = getNS(data)
         keys.append((blob, comment))
     return keys
Ejemplo n.º 19
0
    def processEnded(self, reason):
        """
        Called when the process has ended.

        @param reason: a Failure giving the reason for the process' end.
        """
        if reason.value.exitCode != 0:
            self._getDeferred().errback(
                ConchError("exit code was not 0: {} ({})".format(
                    reason.value.exitCode,
                    self.problems.decode("charmap"),
                )))
        else:
            buf = self.buf.replace(b"\r\n", b"\n")
            self._getDeferred().callback(buf)
Ejemplo n.º 20
0
    def _getPassword(self, prompt):
        """
        Prompt for a password using L{getpass.getpass}.

        @param prompt: Written on tty to ask for the input.
        @type prompt: L{str}
        @return: The input.
        @rtype: L{str}
        """
        with self._replaceStdoutStdin():
            try:
                p = getpass.getpass(prompt)
                return p
            except (KeyboardInterrupt, IOError):
                print()
                raise ConchError('PEBKAC')
Ejemplo n.º 21
0
 def channel_forwarded_tcpip(self, windowSize, maxPacket, data):
     '''
     This gets called when the remote forwared port gets a connection request
     '''
     log.msg('%s %s' % ('FTCP', repr(data)))
     remoteHP, origHP = forwarding.unpackOpen_forwarded_tcpip(data)
     log.msg(self.remoteForwards)
     log.msg(remoteHP)
     if self.remoteForwards.has_key(remoteHP[1]):
         connectHP = self.remoteForwards[remoteHP[1]]
         log.msg('connect forwarding %s' % (connectHP, ))
         return SSHConnectForwardingChannel(connectHP,
                                            remoteWindow=windowSize,
                                            remoteMaxPacket=maxPacket,
                                            conn=self)
     else:
         raise ConchError(connection.OPEN_CONNECT_FAILED,
                          "don't know about that port")
Ejemplo n.º 22
0
 def getPrivateKey(self):
     file = os.path.expanduser(self.usedFiles[-1])
     if not os.path.exists(file):
         return None
     try:
         return defer.succeed(keys.getPrivateKeyObject(file))
     except keys.BadKeyError, e:
         if e.args[0] == 'encrypted key with no passphrase':
             for i in range(3):
                 prompt = "Enter passphrase for key '%s': " % \
                        self.usedFiles[-1]
                 try:
                     p = self._getPassword(prompt)
                     return defer.succeed(
                         keys.getPrivateKeyObject(file, passphrase=p))
                 except (keys.BadKeyError, ConchError):
                     pass
             return defer.fail(ConchError('bad password'))
         raise
Ejemplo n.º 23
0
 def ssh_USERAUTH_REQUEST(self, packet):
     # This is copied and pasted from twisted/conch/ssh/userauth.py in
     # Twisted 8.0.1. We do this so we can add _ebLogToBanner between
     # two existing errbacks.
     user, nextService, method, rest = getNS(packet, 3)
     if user != self.user or nextService != self.nextService:
         self.authenticatedWith = []  # clear auth state
     self.user = user
     self.nextService = nextService
     self.method = method
     d = self.tryAuth(method, user, rest)
     if not d:
         self._ebBadAuth(failure.Failure(ConchError('auth returned none')))
         return
     d.addCallback(self._sendConfiguredBanner)
     d.addCallbacks(self._cbFinishedAuth)
     d.addErrback(self._ebMaybeBadAuth)
     # This line does not appear in the original.
     d.addErrback(self._ebLogToBanner)
     d.addErrback(self._ebBadAuth)
     return d
Ejemplo n.º 24
0
 def _cbSignData(self, data):
     if ord(data[0]) != AGENT_SIGN_RESPONSE:
         raise ConchError('unexpected data: %i' % ord(data[0]))
     signature = getNS(data[1:])[0]
     return signature
    def auth(self, auth_service, argv):
        """Verify we have permission to run the request command."""
        # Key fingerprint
        if hasattr(self.user.meta, "fingerprint"):
            fingerprint = self.user.meta.fingerprint
        else:
            fingerprint = None

        if hasattr(self.user.meta, "password"):
            password = self.user.meta.password
        else:
            password = None

        # Check permissions by mapping requested path to file system path
        repostring = argv[-1]
        repolist = repostring.split('/')
        if repolist[0]:
            # No leading /
            scheme = repolist[0]
            projectpath = repolist[1:]
        else:
            scheme = repolist[1]
            projectpath = repolist[2:]
        repopath = self.user.meta.repopath(scheme, projectpath)
        if not repopath:
            return Failure(
                ConchError(
                    "The remote repository at '{0}' does not exist. Verify that your remote is correct."
                    .format(repostring)))
        projectname = self.user.meta.projectname(repostring)

        # Map the user
        users = auth_service["users"]
        user = self.map_user(self.user.username, fingerprint, users)
        execGitCommand = repopath, user, auth_service

        # Check to see if anonymous read access is enabled and if
        # this is a read
        if (not self.user.meta.anonymousReadAccess or \
                'git-upload-pack' not in argv[:-1]):
            # First, error out if the project itself is disabled.
            if not auth_service["status"]:
                error = "Project {1} has been disabled.".format(projectname)
                return Failure(ConchError(error))
            # If anonymous access for this type of command is not allowed,
            # check if the user is a maintainer on this project
            # global values - d.o issue #1036686
            # "git":key
            if self.user.username == "git" and user and not user["global"]:
                return execGitCommand
            # Username in maintainers list
            elif self.user.username not in users:
                error = "User '{1}' does not have write permissions for repository '{2}'".format(
                    projectname, self.user.username)
                return Failure(ConchError(error))
            elif not user["global"]:
                # username:key
                if fingerprint in user["ssh_keys"].values():
                    return execGitCommand
                # username:password
                elif user["pass"] == password:
                    return execGitCommand
                else:
                    # Both kinds of username auth failed
                    error = "Permission denied when accessing '{1}' as user '{2}'".format(
                        projectname, self.user.username)
                    return Failure(ConchError(error))
            else:
                # Account is globally disabled or disallowed
                # 0x01 = no Git user role, but unknown reason (probably a bug!)
                # 0x02 = Git account suspended
                # 0x04 = Git ToS unchecked
                # 0x08 = Drupal.org account blocked
                error = ''
                if user["global"] & 0x02:
                    error += "Your Git access has been suspended.\n"
                if user["global"] & 0x04:
                    error += "You are required to accept the Git Access Agreement in your user profile before using Git.\n"
                if user["global"] & 0x08:
                    error += "Your Drupal.org account has been blocked.\n"

                if not error and user["global"] == 0x01:
                    error = "You do not have permission to access '{0}' with the provided credentials.\n".format(
                        projectname)
                elif not error:
                    # unknown situation, but be safe and error out
                    error = "This operation cannot be completed at this time.  It may be that we are experiencing technical difficulties or are currently undergoing maintenance."
                return Failure(ConchError(error))
        else:
            # Read only command and anonymous access is enabled
            return execGitCommand
Ejemplo n.º 26
0
 def _cbSignData(self, data):
     if data[0] != chr(AGENT_SIGN_RESPONSE):
         return ConchError('unexpected data: %i' % ord(data[0]))
     signature = getNS(data[1:])[0]
     return signature
Ejemplo n.º 27
0
 def testOpen(self):
     channel = SshClient.CommandChannel('foo')
     channel.openFailed(ConchError('quux', 22))
     self.assertEqual(
         'None CommandChannel Open of foo failed (error code 22): quux',
         SshClient.log.message)
Ejemplo n.º 28
0
    def auth(self, auth_service, argv):
        """Verify we have permission to run the request command."""
        # Key fingerprint
        if hasattr(self.user.meta, "fingerprint"):
            fingerprint = self.user.meta.fingerprint
        else:
            fingerprint = None

        if hasattr(self.user.meta, "password"):
            password = self.user.meta.password
        else:
            password = None

        # Check permissions by mapping requested path to file system path
        repostring = argv[-1]
        repolist = repostring.split('/')
        scheme = repolist[1]
        projectpath = repolist[2:]
        repopath = self.user.meta.repopath(scheme, projectpath)
        if not repopath:
            return Failure(ConchError("The remote repository at '{0}' does not exist. Verify that your remote is correct.".format(repostring)))

        # Map the user
        users = auth_service["users"]
        user = self.map_user(self.user.username, fingerprint, users)

        # Check to see if anonymous read access is enabled and if 
        # this is a read
        if (not self.user.meta.anonymousReadAccess or \
                'git-upload-pack' not in argv[:-1]):
            # If anonymous access for this type of command is not allowed, 
            # check if the user is a maintainer on this project
            # global values - d.o issue #1036686
            # "git":key
            if self.user.username == "git" and user and not user["global"]:
                return repopath, user, auth_service["repo_id"]
            # Username in maintainers list
            elif self.user.username in users and not user["global"]:
                # username:key
                if fingerprint in user["ssh_keys"].values():
                    return repopath, user, auth_service["repo_id"]
                # username:password
                elif user["pass"] == password:
                    return repopath, user, auth_service["repo_id"]
                else:
                    # Both kinds of username auth failed
                    error = "Permission denied when accessing '{1}' as user '{2}'".format(argv[-1], self.user.username)
                    return Failure(ConchError(error))
            else:
                # Account is globally disabled or disallowed
                # 0 = ok, 1 = suspended, 2 = ToS unchecked, 3 = other reason
                if user and user["global"] == 1:
                    error = "Your account is suspended."
                elif user and user["global"] == 2:
                    error = "You are required to accept the Git Access Agreement in your user profile before using git."
                elif user and user["global"] == 3:
                    error = "This operation cannot be completed at this time.  It may be that we are experiencing technical difficulties or are currently undergoing maintenance."
                else:
                    error = "You do not have permission to access '{0}' with the provided credentials.".format(argv[-1])
                return Failure(ConchError(error))
        else:
            # Read only command and anonymous access is enabled
            return repopath, user, auth_service["repo_id"]
Ejemplo n.º 29
0
 def getChannel(self, channelID):
     channel = self.conn.channels[channelID]
     if not isinstance(channel, SSHUnixChannel):
         raise ConchError('nice try bub')
     return channel