Package paramiko :: Module win_pageant |
[frames] | no frames]

# Source Code for Module paramiko.win_pageant

      1  # Copyright (C) 2005 John Arbash-Meinel <john@arbash-meinel.com>
      2  # Modified up by: Todd Whiteman <ToddW@ActiveState.com>
      3  #
      4  # This file is part of paramiko.
      5  #
      6  # Paramiko is free software; you can redistribute it and/or modify it under the
      7  # terms of the GNU Lesser General Public License as published by the Free
      8  # Software Foundation; either version 2.1 of the License, or (at your option)
      9  # any later version.
     10  #
     11  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
     12  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     13  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
     14  # details.
     15  #
     16  # You should have received a copy of the GNU Lesser General Public License
     17  # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
     18  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
     19
     20  """
     21  Functions for communicating with Pageant, the basic windows ssh agent program.
     22  """
     23
     24  import os
     25  import struct
     26  import tempfile
     27  import mmap
     28  import array
     29
     30  # if you're on windows, you should have one of these, i guess?
     31  # ctypes is part of standard library since Python 2.5
     32  _has_win32all = False
     33  _has_ctypes = False
     34  try:
     35      # win32gui is preferred over win32ui to avoid MFC dependencies
     36      import win32gui
     37      _has_win32all = True
     38  except ImportError:
     39      try:
     40          import ctypes
     41          _has_ctypes = True
     42      except ImportError:
     43          pass
     44
     45
     46  _AGENT_COPYDATA_ID = 0x804e50ba
     47  _AGENT_MAX_MSGLEN = 8192
     48  # Note: The WM_COPYDATA value is pulled from win32con, as a workaround
     49  # so we do not have to import this huge library just for this one variable.
     50  win32con_WM_COPYDATA = 74
     51
     52
     53 -def _get_pageant_window_object():
     54      if _has_win32all:
     55          try:
     56              hwnd = win32gui.FindWindow('Pageant', 'Pageant')
     57              return hwnd
     58          except win32gui.error:
     59              pass
     60      elif _has_ctypes:
     61          # Return 0 if there is no Pageant window.
     62          return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant')
     63      return None
     64
     65
     66 -def can_talk_to_agent():
     67      """
     68      Check to see if there is a "Pageant" agent we can talk to.
     69
     70      This checks both if we have the required libraries (win32all or ctypes)
     71      and if there is a Pageant currently running.
     72      """
     73      if (_has_win32all or _has_ctypes) and _get_pageant_window_object():
     74          return True
     75      return False
     76
     77
     78 -def _query_pageant(msg):
     79      hwnd = _get_pageant_window_object()
     80      if not hwnd:
     81          # Raise a failure to connect exception, pageant isn't running anymore!
     82          return None
     83
     84      # Write our pageant request string into the file (pageant will read this to determine what to do)
     85      filename = tempfile.mktemp('.pag')
     86      map_filename = os.path.basename(filename)
     87
     88      f = open(filename, 'w+b')
     89      f.write(msg )
     90      # Ensure the rest of the file is empty, otherwise pageant will read this
     91      f.write('\0' * (_AGENT_MAX_MSGLEN - len(msg)))
     92      # Create the shared file map that pageant will use to read from
     93      pymap = mmap.mmap(f.fileno(), _AGENT_MAX_MSGLEN, tagname=map_filename, access=mmap.ACCESS_WRITE)
     94      try:
     95          # Create an array buffer containing the mapped filename
     96          char_buffer = array.array("c", map_filename + '\0')
     97          char_buffer_address, char_buffer_size = char_buffer.buffer_info()
     98          # Create a string to use for the SendMessage function call
     99          cds = struct.pack("LLP", _AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address)
    100
    101          if _has_win32all:
    102              # win32gui.SendMessage should also allow the same pattern as
    103              # ctypes, but let's keep it like this for now...
    104              response = win32gui.SendMessage(hwnd, win32con_WM_COPYDATA, len(cds), cds)
    105          elif _has_ctypes:
    106              _buf = array.array('B', cds)
    107              _addr, _size = _buf.buffer_info()
    108              response = ctypes.windll.user32.SendMessageA(hwnd, win32con_WM_COPYDATA, _size, _addr)
    109          else:
    110              response = 0
    111
    112          if response > 0:
    113              datalen = pymap.read(4)
    114              retlen = struct.unpack('>I', datalen)[0]
    115              return datalen + pymap.read(retlen)
    116          return None
    117      finally:
    118          pymap.close()
    119          f.close()
    120          # Remove the file, it was temporary only
    121          os.unlink(filename)
    122
    123
    124 -class PageantConnection (object):
    125      """
    126      Mock "connection" to an agent which roughly approximates the behavior of
    127      a unix local-domain socket (as used by Agent).  Requests are sent to the
    128      pageant daemon via special Windows magick, and responses are buffered back
    129      for subsequent reads.
    130      """
    131
    132 -    def __init__(self):
    133          self._response = None
    134
    135 -    def send(self, data):
    136          self._response = _query_pageant(data)
    137
    138 -    def recv(self, n):
    139          if self._response is None:
    140              return ''
    141          ret = self._response[:n]
    142          self._response = self._response[n:]
    143          if self._response == '':
    144              self._response = None
    145          return ret
    146
    147 -    def close(self):
    148          pass
    149