kex_gss.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
  2. # Copyright (C) 2013-2014 science + computing ag
  3. # Author: Sebastian Deiss <sebastian.deiss@t-online.de>
  4. #
  5. #
  6. # This file is part of paramiko.
  7. #
  8. # Paramiko is free software; you can redistribute it and/or modify it under the
  9. # terms of the GNU Lesser General Public License as published by the Free
  10. # Software Foundation; either version 2.1 of the License, or (at your option)
  11. # any later version.
  12. #
  13. # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
  14. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  15. # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  16. # details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License
  19. # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
  20. # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21. """
  22. This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`.
  23. .. note:: Credential delegation is not supported in server mode.
  24. .. note::
  25. `RFC 4462 Section 2.2
  26. <https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not
  27. required to implement GSS-API error messages. Thus, in many methods within
  28. this module, if an error occurs an exception will be thrown and the
  29. connection will be terminated.
  30. .. seealso:: :doc:`/api/ssh_gss`
  31. .. versionadded:: 1.15
  32. """
  33. import os
  34. from hashlib import sha1
  35. from paramiko.common import DEBUG, max_byte, zero_byte
  36. from paramiko import util
  37. from paramiko.message import Message
  38. from paramiko.py3compat import byte_chr, byte_mask, byte_ord
  39. from paramiko.ssh_exception import SSHException
  40. MSG_KEXGSS_INIT, MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_HOSTKEY,\
  41. MSG_KEXGSS_ERROR = range(30, 35)
  42. MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP = range(40, 42)
  43. c_MSG_KEXGSS_INIT, c_MSG_KEXGSS_CONTINUE, c_MSG_KEXGSS_COMPLETE,\
  44. c_MSG_KEXGSS_HOSTKEY, c_MSG_KEXGSS_ERROR = [
  45. byte_chr(c) for c in range(30, 35)
  46. ]
  47. c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP = [
  48. byte_chr(c) for c in range(40, 42)
  49. ]
  50. class KexGSSGroup1(object):
  51. """
  52. GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC
  53. 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
  54. """
  55. # draft-ietf-secsh-transport-09.txt, page 17
  56. P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
  57. G = 2
  58. b7fffffffffffffff = byte_chr(0x7f) + max_byte * 7 # noqa
  59. b0000000000000000 = zero_byte * 8 # noqa
  60. NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g=="
  61. def __init__(self, transport):
  62. self.transport = transport
  63. self.kexgss = self.transport.kexgss_ctxt
  64. self.gss_host = None
  65. self.x = 0
  66. self.e = 0
  67. self.f = 0
  68. def start_kex(self):
  69. """
  70. Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange.
  71. """
  72. self.transport.gss_kex_used = True
  73. self._generate_x()
  74. if self.transport.server_mode:
  75. # compute f = g^x mod p, but don't send it yet
  76. self.f = pow(self.G, self.x, self.P)
  77. self.transport._expect_packet(MSG_KEXGSS_INIT)
  78. return
  79. # compute e = g^x mod p (where g=2), and send it
  80. self.e = pow(self.G, self.x, self.P)
  81. # Initialize GSS-API Key Exchange
  82. self.gss_host = self.transport.gss_host
  83. m = Message()
  84. m.add_byte(c_MSG_KEXGSS_INIT)
  85. m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
  86. m.add_mpint(self.e)
  87. self.transport._send_message(m)
  88. self.transport._expect_packet(MSG_KEXGSS_HOSTKEY,
  89. MSG_KEXGSS_CONTINUE,
  90. MSG_KEXGSS_COMPLETE,
  91. MSG_KEXGSS_ERROR)
  92. def parse_next(self, ptype, m):
  93. """
  94. Parse the next packet.
  95. :param ptype: The (string) type of the incoming packet
  96. :param `.Message` m: The paket content
  97. """
  98. if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT):
  99. return self._parse_kexgss_init(m)
  100. elif not self.transport.server_mode and (ptype == MSG_KEXGSS_HOSTKEY):
  101. return self._parse_kexgss_hostkey(m)
  102. elif self.transport.server_mode and (ptype == MSG_KEXGSS_CONTINUE):
  103. return self._parse_kexgss_continue(m)
  104. elif not self.transport.server_mode and (ptype == MSG_KEXGSS_COMPLETE):
  105. return self._parse_kexgss_complete(m)
  106. elif ptype == MSG_KEXGSS_ERROR:
  107. return self._parse_kexgss_error(m)
  108. raise SSHException('GSS KexGroup1 asked to handle packet type %d'
  109. % ptype)
  110. # ## internals...
  111. def _generate_x(self):
  112. """
  113. generate an "x" (1 < x < q), where q is (p-1)/2.
  114. p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
  115. therefore q can be approximated as a 2^1023. we drop the subset of
  116. potential x where the first 63 bits are 1, because some of those will
  117. be larger than q (but this is a tiny tiny subset of potential x).
  118. """
  119. while 1:
  120. x_bytes = os.urandom(128)
  121. x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:]
  122. first = x_bytes[:8]
  123. if first not in (self.b7fffffffffffffff, self.b0000000000000000):
  124. break
  125. self.x = util.inflate_long(x_bytes)
  126. def _parse_kexgss_hostkey(self, m):
  127. """
  128. Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
  129. :param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
  130. """
  131. # client mode
  132. host_key = m.get_string()
  133. self.transport.host_key = host_key
  134. sig = m.get_string()
  135. self.transport._verify_key(host_key, sig)
  136. self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
  137. MSG_KEXGSS_COMPLETE)
  138. def _parse_kexgss_continue(self, m):
  139. """
  140. Parse the SSH2_MSG_KEXGSS_CONTINUE message.
  141. :param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE
  142. message
  143. """
  144. if not self.transport.server_mode:
  145. srv_token = m.get_string()
  146. m = Message()
  147. m.add_byte(c_MSG_KEXGSS_CONTINUE)
  148. m.add_string(self.kexgss.ssh_init_sec_context(
  149. target=self.gss_host, recv_token=srv_token))
  150. self.transport.send_message(m)
  151. self.transport._expect_packet(
  152. MSG_KEXGSS_CONTINUE,
  153. MSG_KEXGSS_COMPLETE,
  154. MSG_KEXGSS_ERROR
  155. )
  156. else:
  157. pass
  158. def _parse_kexgss_complete(self, m):
  159. """
  160. Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
  161. :param `.Message` m: The content of the
  162. SSH2_MSG_KEXGSS_COMPLETE message
  163. """
  164. # client mode
  165. if self.transport.host_key is None:
  166. self.transport.host_key = NullHostKey()
  167. self.f = m.get_mpint()
  168. if (self.f < 1) or (self.f > self.P - 1):
  169. raise SSHException('Server kex "f" is out of range')
  170. mic_token = m.get_string()
  171. # This must be TRUE, if there is a GSS-API token in this message.
  172. bool = m.get_boolean()
  173. srv_token = None
  174. if bool:
  175. srv_token = m.get_string()
  176. K = pow(self.f, self.x, self.P)
  177. # okay, build up the hash H of
  178. # (V_C || V_S || I_C || I_S || K_S || e || f || K)
  179. hm = Message()
  180. hm.add(self.transport.local_version, self.transport.remote_version,
  181. self.transport.local_kex_init, self.transport.remote_kex_init)
  182. hm.add_string(self.transport.host_key.__str__())
  183. hm.add_mpint(self.e)
  184. hm.add_mpint(self.f)
  185. hm.add_mpint(K)
  186. self.transport._set_K_H(K, sha1(str(hm)).digest())
  187. if srv_token is not None:
  188. self.kexgss.ssh_init_sec_context(target=self.gss_host,
  189. recv_token=srv_token)
  190. self.kexgss.ssh_check_mic(mic_token,
  191. self.transport.session_id)
  192. else:
  193. self.kexgss.ssh_check_mic(mic_token,
  194. self.transport.session_id)
  195. self.transport._activate_outbound()
  196. def _parse_kexgss_init(self, m):
  197. """
  198. Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
  199. :param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
  200. """
  201. # server mode
  202. client_token = m.get_string()
  203. self.e = m.get_mpint()
  204. if (self.e < 1) or (self.e > self.P - 1):
  205. raise SSHException('Client kex "e" is out of range')
  206. K = pow(self.e, self.x, self.P)
  207. self.transport.host_key = NullHostKey()
  208. key = self.transport.host_key.__str__()
  209. # okay, build up the hash H of
  210. # (V_C || V_S || I_C || I_S || K_S || e || f || K)
  211. hm = Message()
  212. hm.add(self.transport.remote_version, self.transport.local_version,
  213. self.transport.remote_kex_init, self.transport.local_kex_init)
  214. hm.add_string(key)
  215. hm.add_mpint(self.e)
  216. hm.add_mpint(self.f)
  217. hm.add_mpint(K)
  218. H = sha1(hm.asbytes()).digest()
  219. self.transport._set_K_H(K, H)
  220. srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host,
  221. client_token)
  222. m = Message()
  223. if self.kexgss._gss_srv_ctxt_status:
  224. mic_token = self.kexgss.ssh_get_mic(self.transport.session_id,
  225. gss_kex=True)
  226. m.add_byte(c_MSG_KEXGSS_COMPLETE)
  227. m.add_mpint(self.f)
  228. m.add_string(mic_token)
  229. if srv_token is not None:
  230. m.add_boolean(True)
  231. m.add_string(srv_token)
  232. else:
  233. m.add_boolean(False)
  234. self.transport._send_message(m)
  235. self.transport._activate_outbound()
  236. else:
  237. m.add_byte(c_MSG_KEXGSS_CONTINUE)
  238. m.add_string(srv_token)
  239. self.transport._send_message(m)
  240. self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
  241. MSG_KEXGSS_COMPLETE,
  242. MSG_KEXGSS_ERROR)
  243. def _parse_kexgss_error(self, m):
  244. """
  245. Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
  246. The server may send a GSS-API error message. if it does, we display
  247. the error by throwing an exception (client mode).
  248. :param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
  249. :raise SSHException: Contains GSS-API major and minor status as well as
  250. the error message and the language tag of the
  251. message
  252. """
  253. maj_status = m.get_int()
  254. min_status = m.get_int()
  255. err_msg = m.get_string()
  256. m.get_string() # we don't care about the language!
  257. raise SSHException("GSS-API Error:\nMajor Status: %s\nMinor Status: %s\
  258. \nError Message: %s\n") % (str(maj_status),
  259. str(min_status),
  260. err_msg)
  261. class KexGSSGroup14(KexGSSGroup1):
  262. """
  263. GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined
  264. in `RFC 4462 Section 2
  265. <https://tools.ietf.org/html/rfc4462.html#section-2>`_
  266. """
  267. P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
  268. G = 2
  269. NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="
  270. class KexGSSGex(object):
  271. """
  272. GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in
  273. `RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
  274. """
  275. NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g=="
  276. min_bits = 1024
  277. max_bits = 8192
  278. preferred_bits = 2048
  279. def __init__(self, transport):
  280. self.transport = transport
  281. self.kexgss = self.transport.kexgss_ctxt
  282. self.gss_host = None
  283. self.p = None
  284. self.q = None
  285. self.g = None
  286. self.x = None
  287. self.e = None
  288. self.f = None
  289. self.old_style = False
  290. def start_kex(self):
  291. """
  292. Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange
  293. """
  294. self.transport.gss_kex_used = True
  295. if self.transport.server_mode:
  296. self.transport._expect_packet(MSG_KEXGSS_GROUPREQ)
  297. return
  298. # request a bit range: we accept (min_bits) to (max_bits), but prefer
  299. # (preferred_bits). according to the spec, we shouldn't pull the
  300. # minimum up above 1024.
  301. self.gss_host = self.transport.gss_host
  302. m = Message()
  303. m.add_byte(c_MSG_KEXGSS_GROUPREQ)
  304. m.add_int(self.min_bits)
  305. m.add_int(self.preferred_bits)
  306. m.add_int(self.max_bits)
  307. self.transport._send_message(m)
  308. self.transport._expect_packet(MSG_KEXGSS_GROUP)
  309. def parse_next(self, ptype, m):
  310. """
  311. Parse the next packet.
  312. :param ptype: The (string) type of the incoming packet
  313. :param `.Message` m: The paket content
  314. """
  315. if ptype == MSG_KEXGSS_GROUPREQ:
  316. return self._parse_kexgss_groupreq(m)
  317. elif ptype == MSG_KEXGSS_GROUP:
  318. return self._parse_kexgss_group(m)
  319. elif ptype == MSG_KEXGSS_INIT:
  320. return self._parse_kexgss_gex_init(m)
  321. elif ptype == MSG_KEXGSS_HOSTKEY:
  322. return self._parse_kexgss_hostkey(m)
  323. elif ptype == MSG_KEXGSS_CONTINUE:
  324. return self._parse_kexgss_continue(m)
  325. elif ptype == MSG_KEXGSS_COMPLETE:
  326. return self._parse_kexgss_complete(m)
  327. elif ptype == MSG_KEXGSS_ERROR:
  328. return self._parse_kexgss_error(m)
  329. raise SSHException('KexGex asked to handle packet type %d' % ptype)
  330. # ## internals...
  331. def _generate_x(self):
  332. # generate an "x" (1 < x < (p-1)/2).
  333. q = (self.p - 1) // 2
  334. qnorm = util.deflate_long(q, 0)
  335. qhbyte = byte_ord(qnorm[0])
  336. byte_count = len(qnorm)
  337. qmask = 0xff
  338. while not (qhbyte & 0x80):
  339. qhbyte <<= 1
  340. qmask >>= 1
  341. while True:
  342. x_bytes = os.urandom(byte_count)
  343. x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
  344. x = util.inflate_long(x_bytes, 1)
  345. if (x > 1) and (x < q):
  346. break
  347. self.x = x
  348. def _parse_kexgss_groupreq(self, m):
  349. """
  350. Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode).
  351. :param `.Message` m: The content of the
  352. SSH2_MSG_KEXGSS_GROUPREQ message
  353. """
  354. minbits = m.get_int()
  355. preferredbits = m.get_int()
  356. maxbits = m.get_int()
  357. # smoosh the user's preferred size into our own limits
  358. if preferredbits > self.max_bits:
  359. preferredbits = self.max_bits
  360. if preferredbits < self.min_bits:
  361. preferredbits = self.min_bits
  362. # fix min/max if they're inconsistent. technically, we could just pout
  363. # and hang up, but there's no harm in giving them the benefit of the
  364. # doubt and just picking a bitsize for them.
  365. if minbits > preferredbits:
  366. minbits = preferredbits
  367. if maxbits < preferredbits:
  368. maxbits = preferredbits
  369. # now save a copy
  370. self.min_bits = minbits
  371. self.preferred_bits = preferredbits
  372. self.max_bits = maxbits
  373. # generate prime
  374. pack = self.transport._get_modulus_pack()
  375. if pack is None:
  376. raise SSHException(
  377. 'Can\'t do server-side gex with no modulus pack')
  378. self.transport._log(
  379. DEBUG, # noqa
  380. 'Picking p (%d <= %d <= %d bits)' % (
  381. minbits, preferredbits, maxbits))
  382. self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
  383. m = Message()
  384. m.add_byte(c_MSG_KEXGSS_GROUP)
  385. m.add_mpint(self.p)
  386. m.add_mpint(self.g)
  387. self.transport._send_message(m)
  388. self.transport._expect_packet(MSG_KEXGSS_INIT)
  389. def _parse_kexgss_group(self, m):
  390. """
  391. Parse the SSH2_MSG_KEXGSS_GROUP message (client mode).
  392. :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message
  393. """
  394. self.p = m.get_mpint()
  395. self.g = m.get_mpint()
  396. # reject if p's bit length < 1024 or > 8192
  397. bitlen = util.bit_length(self.p)
  398. if (bitlen < 1024) or (bitlen > 8192):
  399. raise SSHException(
  400. 'Server-generated gex p (don\'t ask) is out of range '
  401. '(%d bits)' % bitlen)
  402. self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) # noqa
  403. self._generate_x()
  404. # now compute e = g^x mod p
  405. self.e = pow(self.g, self.x, self.p)
  406. m = Message()
  407. m.add_byte(c_MSG_KEXGSS_INIT)
  408. m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
  409. m.add_mpint(self.e)
  410. self.transport._send_message(m)
  411. self.transport._expect_packet(MSG_KEXGSS_HOSTKEY,
  412. MSG_KEXGSS_CONTINUE,
  413. MSG_KEXGSS_COMPLETE,
  414. MSG_KEXGSS_ERROR)
  415. def _parse_kexgss_gex_init(self, m):
  416. """
  417. Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
  418. :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
  419. """
  420. client_token = m.get_string()
  421. self.e = m.get_mpint()
  422. if (self.e < 1) or (self.e > self.p - 1):
  423. raise SSHException('Client kex "e" is out of range')
  424. self._generate_x()
  425. self.f = pow(self.g, self.x, self.p)
  426. K = pow(self.e, self.x, self.p)
  427. self.transport.host_key = NullHostKey()
  428. key = self.transport.host_key.__str__()
  429. # okay, build up the hash H of
  430. # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
  431. hm = Message()
  432. hm.add(self.transport.remote_version, self.transport.local_version,
  433. self.transport.remote_kex_init, self.transport.local_kex_init,
  434. key)
  435. hm.add_int(self.min_bits)
  436. hm.add_int(self.preferred_bits)
  437. hm.add_int(self.max_bits)
  438. hm.add_mpint(self.p)
  439. hm.add_mpint(self.g)
  440. hm.add_mpint(self.e)
  441. hm.add_mpint(self.f)
  442. hm.add_mpint(K)
  443. H = sha1(hm.asbytes()).digest()
  444. self.transport._set_K_H(K, H)
  445. srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host,
  446. client_token)
  447. m = Message()
  448. if self.kexgss._gss_srv_ctxt_status:
  449. mic_token = self.kexgss.ssh_get_mic(self.transport.session_id,
  450. gss_kex=True)
  451. m.add_byte(c_MSG_KEXGSS_COMPLETE)
  452. m.add_mpint(self.f)
  453. m.add_string(mic_token)
  454. if srv_token is not None:
  455. m.add_boolean(True)
  456. m.add_string(srv_token)
  457. else:
  458. m.add_boolean(False)
  459. self.transport._send_message(m)
  460. self.transport._activate_outbound()
  461. else:
  462. m.add_byte(c_MSG_KEXGSS_CONTINUE)
  463. m.add_string(srv_token)
  464. self.transport._send_message(m)
  465. self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
  466. MSG_KEXGSS_COMPLETE,
  467. MSG_KEXGSS_ERROR)
  468. def _parse_kexgss_hostkey(self, m):
  469. """
  470. Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
  471. :param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
  472. """
  473. # client mode
  474. host_key = m.get_string()
  475. self.transport.host_key = host_key
  476. sig = m.get_string()
  477. self.transport._verify_key(host_key, sig)
  478. self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
  479. MSG_KEXGSS_COMPLETE)
  480. def _parse_kexgss_continue(self, m):
  481. """
  482. Parse the SSH2_MSG_KEXGSS_CONTINUE message.
  483. :param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message
  484. """
  485. if not self.transport.server_mode:
  486. srv_token = m.get_string()
  487. m = Message()
  488. m.add_byte(c_MSG_KEXGSS_CONTINUE)
  489. m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host,
  490. recv_token=srv_token))
  491. self.transport.send_message(m)
  492. self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
  493. MSG_KEXGSS_COMPLETE,
  494. MSG_KEXGSS_ERROR)
  495. else:
  496. pass
  497. def _parse_kexgss_complete(self, m):
  498. """
  499. Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
  500. :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message
  501. """
  502. if self.transport.host_key is None:
  503. self.transport.host_key = NullHostKey()
  504. self.f = m.get_mpint()
  505. mic_token = m.get_string()
  506. # This must be TRUE, if there is a GSS-API token in this message.
  507. bool = m.get_boolean()
  508. srv_token = None
  509. if bool:
  510. srv_token = m.get_string()
  511. if (self.f < 1) or (self.f > self.p - 1):
  512. raise SSHException('Server kex "f" is out of range')
  513. K = pow(self.f, self.x, self.p)
  514. # okay, build up the hash H of
  515. # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
  516. hm = Message()
  517. hm.add(self.transport.local_version, self.transport.remote_version,
  518. self.transport.local_kex_init, self.transport.remote_kex_init,
  519. self.transport.host_key.__str__())
  520. if not self.old_style:
  521. hm.add_int(self.min_bits)
  522. hm.add_int(self.preferred_bits)
  523. if not self.old_style:
  524. hm.add_int(self.max_bits)
  525. hm.add_mpint(self.p)
  526. hm.add_mpint(self.g)
  527. hm.add_mpint(self.e)
  528. hm.add_mpint(self.f)
  529. hm.add_mpint(K)
  530. H = sha1(hm.asbytes()).digest()
  531. self.transport._set_K_H(K, H)
  532. if srv_token is not None:
  533. self.kexgss.ssh_init_sec_context(target=self.gss_host,
  534. recv_token=srv_token)
  535. self.kexgss.ssh_check_mic(mic_token,
  536. self.transport.session_id)
  537. else:
  538. self.kexgss.ssh_check_mic(mic_token,
  539. self.transport.session_id)
  540. self.transport._activate_outbound()
  541. def _parse_kexgss_error(self, m):
  542. """
  543. Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
  544. The server may send a GSS-API error message. if it does, we display
  545. the error by throwing an exception (client mode).
  546. :param `Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
  547. :raise SSHException: Contains GSS-API major and minor status as well as
  548. the error message and the language tag of the
  549. message
  550. """
  551. maj_status = m.get_int()
  552. min_status = m.get_int()
  553. err_msg = m.get_string()
  554. m.get_string() # we don't care about the language (lang_tag)!
  555. raise SSHException("GSS-API Error:\nMajor Status: %s\nMinor Status: %s\
  556. \nError Message: %s\n") % (str(maj_status),
  557. str(min_status),
  558. err_msg)
  559. class NullHostKey(object):
  560. """
  561. This class represents the Null Host Key for GSS-API Key Exchange as defined
  562. in `RFC 4462 Section 5
  563. <https://tools.ietf.org/html/rfc4462.html#section-5>`_
  564. """
  565. def __init__(self):
  566. self.key = ""
  567. def __str__(self):
  568. return self.key
  569. def get_name(self):
  570. return self.key