Exemple #1
0
 def __init__(self,
              msg=None,
              data=None,
              filename=None,
              password=None,
              vals=None,
              file_obj=None):
     self.p = None
     self.q = None
     self.g = None
     self.y = None
     self.x = None
     if file_obj is not None:
         self._from_private_key(file_obj, password)
         return
     if filename is not None:
         self._from_private_key_file(filename, password)
         return
     if (msg is None) and (data is not None):
         msg = Message(data)
     if vals is not None:
         self.p, self.q, self.g, self.y = vals
     else:
         if msg is None:
             raise SSHException('Key object may not be empty')
         if msg.get_string() != 'ssh-dss':
             raise SSHException('Invalid key')
         self.p = msg.get_mpint()
         self.q = msg.get_mpint()
         self.g = msg.get_mpint()
         self.y = msg.get_mpint()
     self.size = util.bit_length(self.p)
Exemple #2
0
 def _read_private_key(self, tag, f, password=None):
     lines = f.readlines()
     start = 0
     while (start < len(lines)) and (lines[start].strip() != '-----BEGIN ' +
                                     tag + ' PRIVATE KEY-----'):
         start += 1
     if start >= len(lines):
         raise SSHException('not a valid ' + tag + ' private key file')
     # parse any headers first
     headers = {}
     start += 1
     while start < len(lines):
         l = lines[start].split(': ')
         if len(l) == 1:
             break
         headers[l[0].lower()] = l[1].strip()
         start += 1
     # find end
     end = start
     while (lines[end].strip() !=
            '-----END ' + tag + ' PRIVATE KEY-----') and (end < len(lines)):
         end += 1
     # if we trudged to the end of the file, just try to cope.
     try:
         data = base64.decodestring(''.join(lines[start:end]))
     except base64.binascii.Error, e:
         raise SSHException('base64 decoding error: ' + str(e))
Exemple #3
0
 def _encode_key(self):
     if self.x is None:
         raise SSHException('Not enough key information')
     keylist = [0, self.p, self.q, self.g, self.y, self.x]
     try:
         b = BER()
         b.encode(keylist)
     except BERException:
         raise SSHException('Unable to create ber encoding of key')
     return str(b)
Exemple #4
0
 def _read_all(self, wanted):
     result = self.conn.recv(wanted)
     while len(result) < wanted:
         if len(result) == 0:
             raise SSHException('lost ssh-agent')
         extra = self.conn.recv(wanted - len(result))
         if len(extra) == 0:
             raise SSHException('lost ssh-agent')
         result += extra
     return result
Exemple #5
0
 def _encode_key(self):
     if (self.p is None) or (self.q is None):
         raise SSHException('Not enough key info to write private key file')
     keylist = [ 0, self.n, self.e, self.d, self.p, self.q,
                 self.d % (self.p - 1), self.d % (self.q - 1),
                 util.mod_inverse(self.q, self.p) ]
     try:
         b = BER()
         b.encode(keylist)
     except BERException:
         raise SSHException('Unable to create ber encoding of key')
     return str(b)
Exemple #6
0
 def _read_response(self, waitfor=None):
     while True:
         try:
             t, data = self._read_packet()
         except EOFError, e:
             raise SSHException('Server connection dropped: %s' %
                                (str(e), ))
         msg = Message(data)
         num = msg.get_int()
         if num not in self._expecting:
             # might be response for a file that was closed before responses came back
             self._log(DEBUG, 'Unexpected response #%d' % (num, ))
             if waitfor is None:
                 # just doing a single check
                 break
             continue
         fileobj = self._expecting[num]
         del self._expecting[num]
         if num == waitfor:
             # synchronous
             if t == CMD_STATUS:
                 self._convert_status(msg)
             return t, msg
         if fileobj is not type(None):
             fileobj._async_response(t, msg)
         if waitfor is None:
             # just doing a single check
             break
Exemple #7
0
 def _parse_kexdh_gex_reply(self, m):
     host_key = m.get_string()
     self.f = m.get_mpint()
     sig = m.get_string()
     if (self.f < 1) or (self.f > self.p - 1):
         raise SSHException('Server kex "f" is out of range')
     K = pow(self.f, self.x, self.p)
     # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
     hm = Message()
     hm.add(self.transport.local_version, self.transport.remote_version,
            self.transport.local_kex_init, self.transport.remote_kex_init,
            host_key)
     if not self.old_style:
         hm.add_int(self.min_bits)
     hm.add_int(self.preferred_bits)
     if not self.old_style:
         hm.add_int(self.max_bits)
     hm.add_mpint(self.p)
     hm.add_mpint(self.g)
     hm.add_mpint(self.e)
     hm.add_mpint(self.f)
     hm.add_mpint(K)
     self.transport._set_K_H(K, SHA.new(str(hm)).digest())
     self.transport._verify_key(host_key, sig)
     self.transport._activate_outbound()
Exemple #8
0
 def _decode_key(self, data):
     # private key file contains:
     # DSAPrivateKey = { version = 0, p, q, g, y, x }
     try:
         keylist = BER(data).decode()
     except BERException, x:
         raise SSHException('Unable to parse key file: ' + str(x))
Exemple #9
0
 def _parse_kexdh_gex_request(self, m):
     minbits = m.get_int()
     preferredbits = m.get_int()
     maxbits = m.get_int()
     # smoosh the user's preferred size into our own limits
     if preferredbits > self.max_bits:
         preferredbits = self.max_bits
     if preferredbits < self.min_bits:
         preferredbits = self.min_bits
     # fix min/max if they're inconsistent.  technically, we could just pout
     # and hang up, but there's no harm in giving them the benefit of the
     # doubt and just picking a bitsize for them.
     if minbits > preferredbits:
         minbits = preferredbits
     if maxbits < preferredbits:
         maxbits = preferredbits
     # now save a copy
     self.min_bits = minbits
     self.preferred_bits = preferredbits
     self.max_bits = maxbits
     # generate prime
     pack = self.transport._get_modulus_pack()
     if pack is None:
         raise SSHException(
             'Can\'t do server-side gex with no modulus pack')
     self.transport._log(
         DEBUG, 'Picking p (%d <= %d <= %d bits)' %
         (minbits, preferredbits, maxbits))
     self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
     m.add_mpint(self.p)
     m.add_mpint(self.g)
     self.transport._send_message(m)
     self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
Exemple #10
0
 def get_modulus(self, min, prefer, max):
     bitsizes = self.pack.keys()
     bitsizes.sort()
     if len(bitsizes) == 0:
         raise SSHException('no moduli available')
     good = -1
     # find nearest bitsize >= preferred
     for b in bitsizes:
         if (b >= prefer) and (b < max) and ((b < good) or (good == -1)):
             good = b
     # if that failed, find greatest bitsize >= min
     if good == -1:
         for b in bitsizes:
             if (b >= min) and (b < max) and (b > good):
                 good = b
     if good == -1:
         # their entire (min, max) range has no intersection with our range.
         # if their range is below ours, pick the smallest.  otherwise pick
         # the largest.  it'll be out of their range requirement either way,
         # but we'll be sending them the closest one we have.
         good = bitsizes[0]
         if min > good:
             good = bitsizes[-1]
     # now pick a random modulus of this bitsize
     n = _roll_random(self.rng, len(self.pack[good]))
     return self.pack[good][n]
Exemple #11
0
    def __init__(self, sock):
        """
        Create an SFTP client from an existing L{Channel}.  The channel
        should already have requested the C{"sftp"} subsystem.

        An alternate way to create an SFTP client context is by using
        L{from_transport}.

        @param sock: an open L{Channel} using the C{"sftp"} subsystem
        @type sock: L{Channel}

        @raise SSHException: if there's an exception while negotiating
            sftp
        """
        BaseSFTP.__init__(self)
        self.sock = sock
        self.ultra_debug = False
        self.request_number = 1
        # lock for request_number
        self._lock = threading.Lock()
        self._cwd = None
        # request # -> SFTPFile
        self._expecting = weakref.WeakValueDictionary()
        if type(sock) is Channel:
            # override default logger
            transport = self.sock.get_transport()
            self.logger = util.get_logger(transport.get_log_channel() +
                                          '.sftp')
            self.ultra_debug = transport.get_hexdump()
        try:
            server_version = self._send_version()
        except EOFError, x:
            raise SSHException('EOF during negotiation')
Exemple #12
0
 def _parse_kexdh_init(self, m):
     # server mode
     self.e = m.get_mpint()
     if (self.e < 1) or (self.e > P - 1):
         raise SSHException('Client kex "e" is out of range')
     K = pow(self.e, self.x, P)
     key = str(self.transport.get_server_key())
     # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
     hm = Message()
     hm.add(self.transport.remote_version, self.transport.local_version,
            self.transport.remote_kex_init, self.transport.local_kex_init)
     hm.add_string(key)
     hm.add_mpint(self.e)
     hm.add_mpint(self.f)
     hm.add_mpint(K)
     H = SHA.new(str(hm)).digest()
     self.transport._set_K_H(K, H)
     # sign it
     sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H)
     # send reply
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_REPLY))
     m.add_string(key)
     m.add_mpint(self.f)
     m.add_string(str(sig))
     self.transport._send_message(m)
     self.transport._activate_outbound()
Exemple #13
0
 def _decode_key(self, data):
     # private key file contains:
     # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
     try:
         keylist = BER(data).decode()
     except BERException:
         raise SSHException('Unable to parse key file')
     if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0):
         raise SSHException('Not a valid RSA private key file (bad ber encoding)')
     self.n = keylist[1]
     self.e = keylist[2]
     self.d = keylist[3]
     # not really needed
     self.p = keylist[4]
     self.q = keylist[5]
     self.size = util.bit_length(self.n)
Exemple #14
0
 def _parse_service_accept(self, m):
     service = m.get_string()
     if service == 'ssh-userauth':
         self.transport._log(DEBUG, 'userauth is OK')
         m = Message()
         m.add_byte(chr(MSG_USERAUTH_REQUEST))
         m.add_string(self.username)
         m.add_string('ssh-connection')
         m.add_string(self.auth_method)
         if self.auth_method == 'password':
             m.add_boolean(False)
             password = self.password
             if isinstance(password, unicode):
                 password = password.encode('UTF-8')
             m.add_string(password)
         elif self.auth_method == 'publickey':
             m.add_boolean(True)
             m.add_string(self.private_key.get_name())
             m.add_string(str(self.private_key))
             blob = self._get_session_blob(self.private_key,
                                           'ssh-connection', self.username)
             sig = self.private_key.sign_ssh_data(self.transport.rng, blob)
             m.add_string(str(sig))
         elif self.auth_method == 'keyboard-interactive':
             m.add_string('')
             m.add_string(self.submethods)
         elif self.auth_method == 'none':
             pass
         else:
             raise SSHException('Unknown auth method "%s"' %
                                self.auth_method)
         self.transport._send_message(m)
     else:
         self.transport._log(DEBUG,
                             'Service request "%s" accepted (?)' % service)
Exemple #15
0
    def resize_pty(self, width=80, height=24):
        """
        Resize the pseudo-terminal.  This can be used to change the width and
        height of the terminal emulation created in a previous L{get_pty} call.

        @param width: new width (in characters) of the terminal screen
        @type width: int
        @param height: new height (in characters) of the terminal screen
        @type height: int

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('window-change')
        m.add_boolean(True)
        m.add_int(width)
        m.add_int(height)
        m.add_int(0).add_int(0)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #16
0
    def invoke_subsystem(self, subsystem):
        """
        Request a subsystem on the server (for example, C{sftp}).  If the
        server allows it, the channel will then be directly connected to the
        requested subsystem.
        
        When the subsystem finishes, the channel will be closed and can't be
        reused.

        @param subsystem: name of the subsystem being requested.
        @type subsystem: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('subsystem')
        m.add_boolean(True)
        m.add_string(subsystem)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #17
0
    def exec_command(self, command):
        """
        Execute a command on the server.  If the server allows it, the channel
        will then be directly connected to the stdin, stdout, and stderr of
        the command being executed.
        
        When the command finishes executing, the channel will be closed and
        can't be reused.  You must open a new channel if you wish to execute
        another command.

        @param command: a shell command to execute.
        @type command: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('exec')
        m.add_boolean(True)
        m.add_string(command)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #18
0
 def invoke_shell(self):
     """
     Request an interactive shell session on this channel.  If the server
     allows it, the channel will then be directly connected to the stdin,
     stdout, and stderr of the shell.
     
     Normally you would call L{get_pty} before this, in which case the
     shell will operate through the pty, and the channel will be connected
     to the stdin and stdout of the pty.
     
     When the shell exits, the channel will be closed and can't be reused.
     You must open a new channel if you wish to open another shell.
     
     @raise SSHException: if the request was rejected or the channel was
         closed
     """
     if self.closed or self.eof_received or self.eof_sent or not self.active:
         raise SSHException('Channel is not open')
     m = Message()
     m.add_byte(chr(MSG_CHANNEL_REQUEST))
     m.add_int(self.remote_chanid)
     m.add_string('shell')
     m.add_boolean(1)
     self._event_pending()
     self.transport._send_user_message(m)
     self._wait_for_event()
Exemple #19
0
    def get_pty(self, term='vt100', width=80, height=24):
        """
        Request a pseudo-terminal from the server.  This is usually used right
        after creating a client channel, to ask the server to provide some
        basic terminal semantics for a shell invoked with L{invoke_shell}.
        It isn't necessary (or desirable) to call this method if you're going
        to exectue a single command with L{exec_command}.

        @param term: the terminal type to emulate (for example, C{'vt100'})
        @type term: str
        @param width: width (in characters) of the terminal screen
        @type width: int
        @param height: height (in characters) of the terminal screen
        @type height: int
        
        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('pty-req')
        m.add_boolean(True)
        m.add_string(term)
        m.add_int(width)
        m.add_int(height)
        # pixel height, width (usually useless)
        m.add_int(0).add_int(0)
        m.add_string('')
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #20
0
 def _wait_for_event(self):
     self.event.wait()
     assert self.event.isSet()
     if self.event_ready:
         return
     e = self.transport.get_exception()
     if e is None:
         e = SSHException('Channel closed.')
     raise e
Exemple #21
0
 def _connect(self, conn):
     self._conn = conn
     ptype, result = self._send_message(chr(SSH2_AGENTC_REQUEST_IDENTITIES))
     if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
         raise SSHException('could not get keys from ssh-agent')
     keys = []
     for i in range(result.get_int()):
         keys.append(AgentKey(self, result.get_string()))
         result.get_string()
     self._keys = tuple(keys)
Exemple #22
0
 def sign_ssh_data(self, rng, data):
     msg = Message()
     msg.add_byte(chr(SSH2_AGENTC_SIGN_REQUEST))
     msg.add_string(self.blob)
     msg.add_string(data)
     msg.add_int(0)
     ptype, result = self.agent._send_message(msg)
     if ptype != SSH2_AGENT_SIGN_RESPONSE:
         raise SSHException('key cannot be used for signing')
     return result.get_string()
Exemple #23
0
 def parse_next(self, ptype, m):
     if ptype == _MSG_KEXDH_GEX_REQUEST:
         return self._parse_kexdh_gex_request(m)
     elif ptype == _MSG_KEXDH_GEX_GROUP:
         return self._parse_kexdh_gex_group(m)
     elif ptype == _MSG_KEXDH_GEX_INIT:
         return self._parse_kexdh_gex_init(m)
     elif ptype == _MSG_KEXDH_GEX_REPLY:
         return self._parse_kexdh_gex_reply(m)
     elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
         return self._parse_kexdh_gex_request_old(m)
     raise SSHException('KexGex asked to handle packet type %d' % ptype)
Exemple #24
0
 def _parse_userauth_info_response(self, m):
     if not self.transport.server_mode:
         raise SSHException('Illegal info response from server')
     n = m.get_int()
     responses = []
     for i in range(n):
         responses.append(m.get_string())
     result = self.transport.server_object.check_auth_interactive_response(
         responses)
     if isinstance(type(result), InteractiveQuery):
         # make interactive query instead of response
         self._interactive_query(result)
         return
     self._send_auth_result(self.auth_username, 'keyboard-interactive',
                            result)
Exemple #25
0
 def _parse_kexdh_gex_group(self, m):
     self.p = m.get_mpint()
     self.g = m.get_mpint()
     # reject if p's bit length < 1024 or > 8192
     bitlen = util.bit_length(self.p)
     if (bitlen < 1024) or (bitlen > 8192):
         raise SSHException(
             'Server-generated gex p (don\'t ask) is out of range (%d bits)'
             % bitlen)
     self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen)
     self._generate_x()
     # now compute e = g^x mod p
     self.e = pow(self.g, self.x, self.p)
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_GEX_INIT))
     m.add_mpint(self.e)
     self.transport._send_message(m)
     self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)
Exemple #26
0
 def _parse_kexdh_reply(self, m):
     # client mode
     host_key = m.get_string()
     self.f = m.get_mpint()
     if (self.f < 1) or (self.f > P - 1):
         raise SSHException('Server kex "f" is out of range')
     sig = m.get_string()
     K = pow(self.f, self.x, P)
     # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
     hm = Message()
     hm.add(self.transport.local_version, self.transport.remote_version,
            self.transport.local_kex_init, self.transport.remote_kex_init)
     hm.add_string(host_key)
     hm.add_mpint(self.e)
     hm.add_mpint(self.f)
     hm.add_mpint(K)
     self.transport._set_K_H(K, SHA.new(str(hm)).digest())
     self.transport._verify_key(host_key, sig)
     self.transport._activate_outbound()
Exemple #27
0
    def _parse_userauth_info_request(self, m):
        if self.auth_method != 'keyboard-interactive':
            raise SSHException('Illegal info request from server')
        title = m.get_string()
        instructions = m.get_string()
        m.get_string()  # lang
        prompts = m.get_int()
        prompt_list = []
        for i in range(prompts):
            prompt_list.append((m.get_string(), m.get_boolean()))
        response_list = self.interactive_handler(title, instructions,
                                                 prompt_list)

        m = Message()
        m.add_byte(chr(MSG_USERAUTH_INFO_RESPONSE))
        m.add_int(len(response_list))
        for r in response_list:
            m.add_string(r)
        self.transport._send_message(m)
Exemple #28
0
 def __init__(self):
     """
     Open a session with the local machine's SSH agent, if one is running.
     If no agent is running, initialization will succeed, but L{get_keys}
     will return an empty tuple.
     
     @raise SSHException: if an SSH agent is found, but speaks an
         incompatible protocol
     """
     self.conn = None
     self.keys = ()
     if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
         conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
         try:
             conn.connect(os.environ['SSH_AUTH_SOCK'])
         except:
             # probably a dangling env var: the ssh agent is gone
             return
         self.conn = conn
     elif sys.platform == 'win32':
         import win_pageant
         if win_pageant.can_talk_to_agent():
             self.conn = win_pageant.PageantConnection()
         else:
             return
     else:
         # no agent support
         return
         
     ptype, result = self._send_message(chr(SSH2_AGENTC_REQUEST_IDENTITIES))
     if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
         raise SSHException('could not get keys from ssh-agent')
     keys = []
     for i in range(result.get_int()):
         keys.append(AgentKey(self, result.get_string()))
         result.get_string()
     self.keys = tuple(keys)
Exemple #29
0
 def _parse_kexdh_gex_request_old(self, m):
     # same as above, but without min_bits or max_bits (used by older clients like putty)
     self.preferred_bits = m.get_int()
     # smoosh the user's preferred size into our own limits
     if self.preferred_bits > self.max_bits:
         self.preferred_bits = self.max_bits
     if self.preferred_bits < self.min_bits:
         self.preferred_bits = self.min_bits
     # generate prime
     pack = self.transport._get_modulus_pack()
     if pack is None:
         raise SSHException(
             'Can\'t do server-side gex with no modulus pack')
     self.transport._log(DEBUG,
                         'Picking p (~ %d bits)' % (self.preferred_bits, ))
     self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits,
                                       self.max_bits)
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
     m.add_mpint(self.p)
     m.add_mpint(self.g)
     self.transport._send_message(m)
     self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
     self.old_style = True
Exemple #30
0
 def _parse_kexdh_gex_init(self, m):
     self.e = m.get_mpint()
     if (self.e < 1) or (self.e > self.p - 1):
         raise SSHException('Client kex "e" is out of range')
     self._generate_x()
     self.f = pow(self.g, self.x, self.p)
     K = pow(self.e, self.x, self.p)
     key = str(self.transport.get_server_key())
     # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
     hm = Message()
     hm.add(self.transport.remote_version, self.transport.local_version,
            self.transport.remote_kex_init, self.transport.local_kex_init,
            key)
     if not self.old_style:
         hm.add_int(self.min_bits)
     hm.add_int(self.preferred_bits)
     if not self.old_style:
         hm.add_int(self.max_bits)
     hm.add_mpint(self.p)
     hm.add_mpint(self.g)
     hm.add_mpint(self.e)
     hm.add_mpint(self.f)
     hm.add_mpint(K)
     H = SHA.new(str(hm)).digest()
     self.transport._set_K_H(K, H)
     # sign it
     sig = self.transport.get_server_key().sign_ssh_data(
         self.transport.rng, H)
     # send reply
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_GEX_REPLY))
     m.add_string(key)
     m.add_mpint(self.f)
     m.add_string(str(sig))
     self.transport._send_message(m)
     self.transport._activate_outbound()