win_pageant.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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 distributed 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. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. """
  20. Functions for communicating with Pageant, the basic windows ssh agent program.
  21. """
  22. import array
  23. import ctypes.wintypes
  24. import platform
  25. import struct
  26. from paramiko.common import zero_byte
  27. from paramiko.py3compat import b
  28. try:
  29. import _thread as thread # Python 3.x
  30. except ImportError:
  31. import thread # Python 2.5-2.7
  32. from . import _winapi
  33. _AGENT_COPYDATA_ID = 0x804e50ba
  34. _AGENT_MAX_MSGLEN = 8192
  35. # Note: The WM_COPYDATA value is pulled from win32con, as a workaround
  36. # so we do not have to import this huge library just for this one variable.
  37. win32con_WM_COPYDATA = 74
  38. def _get_pageant_window_object():
  39. return ctypes.windll.user32.FindWindowA(b"Pageant", b"Pageant")
  40. def can_talk_to_agent():
  41. """
  42. Check to see if there is a "Pageant" agent we can talk to.
  43. This checks both if we have the required libraries (win32all or ctypes)
  44. and if there is a Pageant currently running.
  45. """
  46. return bool(_get_pageant_window_object())
  47. if platform.architecture()[0] == "64bit":
  48. ULONG_PTR = ctypes.c_uint64
  49. else:
  50. ULONG_PTR = ctypes.c_uint32
  51. class COPYDATASTRUCT(ctypes.Structure):
  52. """
  53. ctypes implementation of
  54. http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx
  55. """
  56. _fields_ = [
  57. ("num_data", ULONG_PTR),
  58. ("data_size", ctypes.wintypes.DWORD),
  59. ("data_loc", ctypes.c_void_p),
  60. ]
  61. def _query_pageant(msg):
  62. """
  63. Communication with the Pageant process is done through a shared
  64. memory-mapped file.
  65. """
  66. hwnd = _get_pageant_window_object()
  67. if not hwnd:
  68. # Raise a failure to connect exception, pageant isn't running anymore!
  69. return None
  70. # create a name for the mmap
  71. map_name = "PageantRequest%08x" % thread.get_ident()
  72. pymap = _winapi.MemoryMap(
  73. map_name, _AGENT_MAX_MSGLEN, _winapi.get_security_attributes_for_user()
  74. )
  75. with pymap:
  76. pymap.write(msg)
  77. # Create an array buffer containing the mapped filename
  78. char_buffer = array.array("b", b(map_name) + zero_byte) # noqa
  79. char_buffer_address, char_buffer_size = char_buffer.buffer_info()
  80. # Create a string to use for the SendMessage function call
  81. cds = COPYDATASTRUCT(
  82. _AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address
  83. )
  84. response = ctypes.windll.user32.SendMessageA(
  85. hwnd, win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds)
  86. )
  87. if response > 0:
  88. pymap.seek(0)
  89. datalen = pymap.read(4)
  90. retlen = struct.unpack(">I", datalen)[0]
  91. return datalen + pymap.read(retlen)
  92. return None
  93. class PageantConnection(object):
  94. """
  95. Mock "connection" to an agent which roughly approximates the behavior of
  96. a unix local-domain socket (as used by Agent). Requests are sent to the
  97. pageant daemon via special Windows magick, and responses are buffered back
  98. for subsequent reads.
  99. """
  100. def __init__(self):
  101. self._response = None
  102. def send(self, data):
  103. self._response = _query_pageant(data)
  104. def recv(self, n):
  105. if self._response is None:
  106. return ""
  107. ret = self._response[:n]
  108. self._response = self._response[n:]
  109. if self._response == "":
  110. self._response = None
  111. return ret
  112. def close(self):
  113. pass