kex_gss.py 24 KB

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