Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/kex_gss.py: 14%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

347 statements  

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 

23""" 

24This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`. 

25 

26.. note:: Credential delegation is not supported in server mode. 

27 

28.. note:: 

29 `RFC 4462 Section 2.2 

30 <https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not 

31 required to implement GSS-API error messages. Thus, in many methods within 

32 this module, if an error occurs an exception will be thrown and the 

33 connection will be terminated. 

34 

35.. seealso:: :doc:`/api/ssh_gss` 

36 

37.. versionadded:: 1.15 

38""" 

39 

40import os 

41from hashlib import sha1 

42 

43from paramiko.common import ( 

44 DEBUG, 

45 max_byte, 

46 zero_byte, 

47 byte_chr, 

48 byte_mask, 

49 byte_ord, 

50) 

51from paramiko import util 

52from paramiko.message import Message 

53from paramiko.ssh_exception import SSHException 

54 

55 

56( 

57 MSG_KEXGSS_INIT, 

58 MSG_KEXGSS_CONTINUE, 

59 MSG_KEXGSS_COMPLETE, 

60 MSG_KEXGSS_HOSTKEY, 

61 MSG_KEXGSS_ERROR, 

62) = range(30, 35) 

63(MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP) = range(40, 42) 

64( 

65 c_MSG_KEXGSS_INIT, 

66 c_MSG_KEXGSS_CONTINUE, 

67 c_MSG_KEXGSS_COMPLETE, 

68 c_MSG_KEXGSS_HOSTKEY, 

69 c_MSG_KEXGSS_ERROR, 

70) = [byte_chr(c) for c in range(30, 35)] 

71(c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP) = [ 

72 byte_chr(c) for c in range(40, 42) 

73] 

74 

75 

76class KexGSSGroup1: 

77 """ 

78 GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC 

79 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_ 

80 """ 

81 

82 # draft-ietf-secsh-transport-09.txt, page 17 

83 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa 

84 G = 2 

85 b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7 # noqa 

86 b0000000000000000 = zero_byte * 8 # noqa 

87 NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" 

88 

89 def __init__(self, transport): 

90 self.transport = transport 

91 self.kexgss = self.transport.kexgss_ctxt 

92 self.gss_host = None 

93 self.x = 0 

94 self.e = 0 

95 self.f = 0 

96 

97 def start_kex(self): 

98 """ 

99 Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange. 

100 """ 

101 self._generate_x() 

102 if self.transport.server_mode: 

103 # compute f = g^x mod p, but don't send it yet 

104 self.f = pow(self.G, self.x, self.P) 

105 self.transport._expect_packet(MSG_KEXGSS_INIT) 

106 return 

107 # compute e = g^x mod p (where g=2), and send it 

108 self.e = pow(self.G, self.x, self.P) 

109 # Initialize GSS-API Key Exchange 

110 self.gss_host = self.transport.gss_host 

111 m = Message() 

112 m.add_byte(c_MSG_KEXGSS_INIT) 

113 m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host)) 

114 m.add_mpint(self.e) 

115 self.transport._send_message(m) 

116 self.transport._expect_packet( 

117 MSG_KEXGSS_HOSTKEY, 

118 MSG_KEXGSS_CONTINUE, 

119 MSG_KEXGSS_COMPLETE, 

120 MSG_KEXGSS_ERROR, 

121 ) 

122 

123 def parse_next(self, ptype, m): 

124 """ 

125 Parse the next packet. 

126 

127 :param ptype: The (string) type of the incoming packet 

128 :param `.Message` m: The packet content 

129 """ 

130 if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT): 

131 return self._parse_kexgss_init(m) 

132 elif not self.transport.server_mode and (ptype == MSG_KEXGSS_HOSTKEY): 

133 return self._parse_kexgss_hostkey(m) 

134 elif self.transport.server_mode and (ptype == MSG_KEXGSS_CONTINUE): 

135 return self._parse_kexgss_continue(m) 

136 elif not self.transport.server_mode and (ptype == MSG_KEXGSS_COMPLETE): 

137 return self._parse_kexgss_complete(m) 

138 elif ptype == MSG_KEXGSS_ERROR: 

139 return self._parse_kexgss_error(m) 

140 msg = "GSS KexGroup1 asked to handle packet type {:d}" 

141 raise SSHException(msg.format(ptype)) 

142 

143 # ## internals... 

144 

145 def _generate_x(self): 

146 """ 

147 generate an "x" (1 < x < q), where q is (p-1)/2. 

148 p is a 128-byte (1024-bit) number, where the first 64 bits are 1. 

149 therefore q can be approximated as a 2^1023. we drop the subset of 

150 potential x where the first 63 bits are 1, because some of those will 

151 be larger than q (but this is a tiny tiny subset of potential x). 

152 """ 

153 while 1: 

154 x_bytes = os.urandom(128) 

155 x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:] 

156 first = x_bytes[:8] 

157 if first not in (self.b7fffffffffffffff, self.b0000000000000000): 

158 break 

159 self.x = util.inflate_long(x_bytes) 

160 

161 def _parse_kexgss_hostkey(self, m): 

162 """ 

163 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode). 

164 

165 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message 

166 """ 

167 # client mode 

168 host_key = m.get_string() 

169 self.transport.host_key = host_key 

170 sig = m.get_string() 

171 self.transport._verify_key(host_key, sig) 

172 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE) 

173 

174 def _parse_kexgss_continue(self, m): 

175 """ 

176 Parse the SSH2_MSG_KEXGSS_CONTINUE message. 

177 

178 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE 

179 message 

180 """ 

181 if not self.transport.server_mode: 

182 srv_token = m.get_string() 

183 m = Message() 

184 m.add_byte(c_MSG_KEXGSS_CONTINUE) 

185 m.add_string( 

186 self.kexgss.ssh_init_sec_context( 

187 target=self.gss_host, recv_token=srv_token 

188 ) 

189 ) 

190 self.transport.send_message(m) 

191 self.transport._expect_packet( 

192 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR 

193 ) 

194 else: 

195 pass 

196 

197 def _parse_kexgss_complete(self, m): 

198 """ 

199 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode). 

200 

201 :param `.Message` m: The content of the 

202 SSH2_MSG_KEXGSS_COMPLETE message 

203 """ 

204 # client mode 

205 if self.transport.host_key is None: 

206 self.transport.host_key = NullHostKey() 

207 self.f = m.get_mpint() 

208 if (self.f < 1) or (self.f > self.P - 1): 

209 raise SSHException('Server kex "f" is out of range') 

210 mic_token = m.get_string() 

211 # This must be TRUE, if there is a GSS-API token in this message. 

212 bool = m.get_boolean() 

213 srv_token = None 

214 if bool: 

215 srv_token = m.get_string() 

216 K = pow(self.f, self.x, self.P) 

217 # okay, build up the hash H of 

218 # (V_C || V_S || I_C || I_S || K_S || e || f || K) 

219 hm = Message() 

220 hm.add( 

221 self.transport.local_version, 

222 self.transport.remote_version, 

223 self.transport.local_kex_init, 

224 self.transport.remote_kex_init, 

225 ) 

226 hm.add_string(self.transport.host_key.__str__()) 

227 hm.add_mpint(self.e) 

228 hm.add_mpint(self.f) 

229 hm.add_mpint(K) 

230 H = sha1(str(hm)).digest() 

231 self.transport._set_K_H(K, H) 

232 if srv_token is not None: 

233 self.kexgss.ssh_init_sec_context( 

234 target=self.gss_host, recv_token=srv_token 

235 ) 

236 self.kexgss.ssh_check_mic(mic_token, H) 

237 else: 

238 self.kexgss.ssh_check_mic(mic_token, H) 

239 self.transport.gss_kex_used = True 

240 self.transport._activate_outbound() 

241 

242 def _parse_kexgss_init(self, m): 

243 """ 

244 Parse the SSH2_MSG_KEXGSS_INIT message (server mode). 

245 

246 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message 

247 """ 

248 # server mode 

249 client_token = m.get_string() 

250 self.e = m.get_mpint() 

251 if (self.e < 1) or (self.e > self.P - 1): 

252 raise SSHException('Client kex "e" is out of range') 

253 K = pow(self.e, self.x, self.P) 

254 self.transport.host_key = NullHostKey() 

255 key = self.transport.host_key.__str__() 

256 # okay, build up the hash H of 

257 # (V_C || V_S || I_C || I_S || K_S || e || f || K) 

258 hm = Message() 

259 hm.add( 

260 self.transport.remote_version, 

261 self.transport.local_version, 

262 self.transport.remote_kex_init, 

263 self.transport.local_kex_init, 

264 ) 

265 hm.add_string(key) 

266 hm.add_mpint(self.e) 

267 hm.add_mpint(self.f) 

268 hm.add_mpint(K) 

269 H = sha1(hm.asbytes()).digest() 

270 self.transport._set_K_H(K, H) 

271 srv_token = self.kexgss.ssh_accept_sec_context( 

272 self.gss_host, client_token 

273 ) 

274 m = Message() 

275 if self.kexgss._gss_srv_ctxt_status: 

276 mic_token = self.kexgss.ssh_get_mic( 

277 self.transport.session_id, gss_kex=True 

278 ) 

279 m.add_byte(c_MSG_KEXGSS_COMPLETE) 

280 m.add_mpint(self.f) 

281 m.add_string(mic_token) 

282 if srv_token is not None: 

283 m.add_boolean(True) 

284 m.add_string(srv_token) 

285 else: 

286 m.add_boolean(False) 

287 self.transport._send_message(m) 

288 self.transport.gss_kex_used = True 

289 self.transport._activate_outbound() 

290 else: 

291 m.add_byte(c_MSG_KEXGSS_CONTINUE) 

292 m.add_string(srv_token) 

293 self.transport._send_message(m) 

294 self.transport._expect_packet( 

295 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR 

296 ) 

297 

298 def _parse_kexgss_error(self, m): 

299 """ 

300 Parse the SSH2_MSG_KEXGSS_ERROR message (client mode). 

301 The server may send a GSS-API error message. if it does, we display 

302 the error by throwing an exception (client mode). 

303 

304 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message 

305 :raise SSHException: Contains GSS-API major and minor status as well as 

306 the error message and the language tag of the 

307 message 

308 """ 

309 maj_status = m.get_int() 

310 min_status = m.get_int() 

311 err_msg = m.get_string() 

312 m.get_string() # we don't care about the language! 

313 raise SSHException( 

314 """GSS-API Error: 

315Major Status: {} 

316Minor Status: {} 

317Error Message: {} 

318""".format( 

319 maj_status, min_status, err_msg 

320 ) 

321 ) 

322 

323 

324class KexGSSGroup14(KexGSSGroup1): 

325 """ 

326 GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined 

327 in `RFC 4462 Section 2 

328 <https://tools.ietf.org/html/rfc4462.html#section-2>`_ 

329 """ 

330 

331 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa 

332 G = 2 

333 NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==" 

334 

335 

336class KexGSSGex: 

337 """ 

338 GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in 

339 `RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_ 

340 """ 

341 

342 NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" 

343 min_bits = 1024 

344 max_bits = 8192 

345 preferred_bits = 2048 

346 

347 def __init__(self, transport): 

348 self.transport = transport 

349 self.kexgss = self.transport.kexgss_ctxt 

350 self.gss_host = None 

351 self.p = None 

352 self.q = None 

353 self.g = None 

354 self.x = None 

355 self.e = None 

356 self.f = None 

357 self.old_style = False 

358 

359 def start_kex(self): 

360 """ 

361 Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange 

362 """ 

363 if self.transport.server_mode: 

364 self.transport._expect_packet(MSG_KEXGSS_GROUPREQ) 

365 return 

366 # request a bit range: we accept (min_bits) to (max_bits), but prefer 

367 # (preferred_bits). according to the spec, we shouldn't pull the 

368 # minimum up above 1024. 

369 self.gss_host = self.transport.gss_host 

370 m = Message() 

371 m.add_byte(c_MSG_KEXGSS_GROUPREQ) 

372 m.add_int(self.min_bits) 

373 m.add_int(self.preferred_bits) 

374 m.add_int(self.max_bits) 

375 self.transport._send_message(m) 

376 self.transport._expect_packet(MSG_KEXGSS_GROUP) 

377 

378 def parse_next(self, ptype, m): 

379 """ 

380 Parse the next packet. 

381 

382 :param ptype: The (string) type of the incoming packet 

383 :param `.Message` m: The packet content 

384 """ 

385 if ptype == MSG_KEXGSS_GROUPREQ: 

386 return self._parse_kexgss_groupreq(m) 

387 elif ptype == MSG_KEXGSS_GROUP: 

388 return self._parse_kexgss_group(m) 

389 elif ptype == MSG_KEXGSS_INIT: 

390 return self._parse_kexgss_gex_init(m) 

391 elif ptype == MSG_KEXGSS_HOSTKEY: 

392 return self._parse_kexgss_hostkey(m) 

393 elif ptype == MSG_KEXGSS_CONTINUE: 

394 return self._parse_kexgss_continue(m) 

395 elif ptype == MSG_KEXGSS_COMPLETE: 

396 return self._parse_kexgss_complete(m) 

397 elif ptype == MSG_KEXGSS_ERROR: 

398 return self._parse_kexgss_error(m) 

399 msg = "KexGex asked to handle packet type {:d}" 

400 raise SSHException(msg.format(ptype)) 

401 

402 # ## internals... 

403 

404 def _generate_x(self): 

405 # generate an "x" (1 < x < (p-1)/2). 

406 q = (self.p - 1) // 2 

407 qnorm = util.deflate_long(q, 0) 

408 qhbyte = byte_ord(qnorm[0]) 

409 byte_count = len(qnorm) 

410 qmask = 0xFF 

411 while not (qhbyte & 0x80): 

412 qhbyte <<= 1 

413 qmask >>= 1 

414 while True: 

415 x_bytes = os.urandom(byte_count) 

416 x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:] 

417 x = util.inflate_long(x_bytes, 1) 

418 if (x > 1) and (x < q): 

419 break 

420 self.x = x 

421 

422 def _parse_kexgss_groupreq(self, m): 

423 """ 

424 Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode). 

425 

426 :param `.Message` m: The content of the 

427 SSH2_MSG_KEXGSS_GROUPREQ message 

428 """ 

429 minbits = m.get_int() 

430 preferredbits = m.get_int() 

431 maxbits = m.get_int() 

432 # smoosh the user's preferred size into our own limits 

433 if preferredbits > self.max_bits: 

434 preferredbits = self.max_bits 

435 if preferredbits < self.min_bits: 

436 preferredbits = self.min_bits 

437 # fix min/max if they're inconsistent. technically, we could just pout 

438 # and hang up, but there's no harm in giving them the benefit of the 

439 # doubt and just picking a bitsize for them. 

440 if minbits > preferredbits: 

441 minbits = preferredbits 

442 if maxbits < preferredbits: 

443 maxbits = preferredbits 

444 # now save a copy 

445 self.min_bits = minbits 

446 self.preferred_bits = preferredbits 

447 self.max_bits = maxbits 

448 # generate prime 

449 pack = self.transport._get_modulus_pack() 

450 if pack is None: 

451 raise SSHException("Can't do server-side gex with no modulus pack") 

452 self.transport._log( 

453 DEBUG, # noqa 

454 "Picking p ({} <= {} <= {} bits)".format( 

455 minbits, preferredbits, maxbits 

456 ), 

457 ) 

458 self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) 

459 m = Message() 

460 m.add_byte(c_MSG_KEXGSS_GROUP) 

461 m.add_mpint(self.p) 

462 m.add_mpint(self.g) 

463 self.transport._send_message(m) 

464 self.transport._expect_packet(MSG_KEXGSS_INIT) 

465 

466 def _parse_kexgss_group(self, m): 

467 """ 

468 Parse the SSH2_MSG_KEXGSS_GROUP message (client mode). 

469 

470 :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message 

471 """ 

472 self.p = m.get_mpint() 

473 self.g = m.get_mpint() 

474 # reject if p's bit length < 1024 or > 8192 

475 bitlen = util.bit_length(self.p) 

476 if (bitlen < 1024) or (bitlen > 8192): 

477 raise SSHException( 

478 "Server-generated gex p (don't ask) is out of range " 

479 "({} bits)".format(bitlen) 

480 ) 

481 self.transport._log( 

482 DEBUG, "Got server p ({} bits)".format(bitlen) 

483 ) # noqa 

484 self._generate_x() 

485 # now compute e = g^x mod p 

486 self.e = pow(self.g, self.x, self.p) 

487 m = Message() 

488 m.add_byte(c_MSG_KEXGSS_INIT) 

489 m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host)) 

490 m.add_mpint(self.e) 

491 self.transport._send_message(m) 

492 self.transport._expect_packet( 

493 MSG_KEXGSS_HOSTKEY, 

494 MSG_KEXGSS_CONTINUE, 

495 MSG_KEXGSS_COMPLETE, 

496 MSG_KEXGSS_ERROR, 

497 ) 

498 

499 def _parse_kexgss_gex_init(self, m): 

500 """ 

501 Parse the SSH2_MSG_KEXGSS_INIT message (server mode). 

502 

503 :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message 

504 """ 

505 client_token = m.get_string() 

506 self.e = m.get_mpint() 

507 if (self.e < 1) or (self.e > self.p - 1): 

508 raise SSHException('Client kex "e" is out of range') 

509 self._generate_x() 

510 self.f = pow(self.g, self.x, self.p) 

511 K = pow(self.e, self.x, self.p) 

512 self.transport.host_key = NullHostKey() 

513 key = self.transport.host_key.__str__() 

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( 

518 self.transport.remote_version, 

519 self.transport.local_version, 

520 self.transport.remote_kex_init, 

521 self.transport.local_kex_init, 

522 key, 

523 ) 

524 hm.add_int(self.min_bits) 

525 hm.add_int(self.preferred_bits) 

526 hm.add_int(self.max_bits) 

527 hm.add_mpint(self.p) 

528 hm.add_mpint(self.g) 

529 hm.add_mpint(self.e) 

530 hm.add_mpint(self.f) 

531 hm.add_mpint(K) 

532 H = sha1(hm.asbytes()).digest() 

533 self.transport._set_K_H(K, H) 

534 srv_token = self.kexgss.ssh_accept_sec_context( 

535 self.gss_host, client_token 

536 ) 

537 m = Message() 

538 if self.kexgss._gss_srv_ctxt_status: 

539 mic_token = self.kexgss.ssh_get_mic( 

540 self.transport.session_id, gss_kex=True 

541 ) 

542 m.add_byte(c_MSG_KEXGSS_COMPLETE) 

543 m.add_mpint(self.f) 

544 m.add_string(mic_token) 

545 if srv_token is not None: 

546 m.add_boolean(True) 

547 m.add_string(srv_token) 

548 else: 

549 m.add_boolean(False) 

550 self.transport._send_message(m) 

551 self.transport.gss_kex_used = True 

552 self.transport._activate_outbound() 

553 else: 

554 m.add_byte(c_MSG_KEXGSS_CONTINUE) 

555 m.add_string(srv_token) 

556 self.transport._send_message(m) 

557 self.transport._expect_packet( 

558 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR 

559 ) 

560 

561 def _parse_kexgss_hostkey(self, m): 

562 """ 

563 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode). 

564 

565 :param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message 

566 """ 

567 # client mode 

568 host_key = m.get_string() 

569 self.transport.host_key = host_key 

570 sig = m.get_string() 

571 self.transport._verify_key(host_key, sig) 

572 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE) 

573 

574 def _parse_kexgss_continue(self, m): 

575 """ 

576 Parse the SSH2_MSG_KEXGSS_CONTINUE message. 

577 

578 :param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message 

579 """ 

580 if not self.transport.server_mode: 

581 srv_token = m.get_string() 

582 m = Message() 

583 m.add_byte(c_MSG_KEXGSS_CONTINUE) 

584 m.add_string( 

585 self.kexgss.ssh_init_sec_context( 

586 target=self.gss_host, recv_token=srv_token 

587 ) 

588 ) 

589 self.transport.send_message(m) 

590 self.transport._expect_packet( 

591 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR 

592 ) 

593 else: 

594 pass 

595 

596 def _parse_kexgss_complete(self, m): 

597 """ 

598 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode). 

599 

600 :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message 

601 """ 

602 if self.transport.host_key is None: 

603 self.transport.host_key = NullHostKey() 

604 self.f = m.get_mpint() 

605 mic_token = m.get_string() 

606 # This must be TRUE, if there is a GSS-API token in this message. 

607 bool = m.get_boolean() 

608 srv_token = None 

609 if bool: 

610 srv_token = m.get_string() 

611 if (self.f < 1) or (self.f > self.p - 1): 

612 raise SSHException('Server kex "f" is out of range') 

613 K = pow(self.f, self.x, self.p) 

614 # okay, build up the hash H of 

615 # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa 

616 hm = Message() 

617 hm.add( 

618 self.transport.local_version, 

619 self.transport.remote_version, 

620 self.transport.local_kex_init, 

621 self.transport.remote_kex_init, 

622 self.transport.host_key.__str__(), 

623 ) 

624 if not self.old_style: 

625 hm.add_int(self.min_bits) 

626 hm.add_int(self.preferred_bits) 

627 if not self.old_style: 

628 hm.add_int(self.max_bits) 

629 hm.add_mpint(self.p) 

630 hm.add_mpint(self.g) 

631 hm.add_mpint(self.e) 

632 hm.add_mpint(self.f) 

633 hm.add_mpint(K) 

634 H = sha1(hm.asbytes()).digest() 

635 self.transport._set_K_H(K, H) 

636 if srv_token is not None: 

637 self.kexgss.ssh_init_sec_context( 

638 target=self.gss_host, recv_token=srv_token 

639 ) 

640 self.kexgss.ssh_check_mic(mic_token, H) 

641 else: 

642 self.kexgss.ssh_check_mic(mic_token, H) 

643 self.transport.gss_kex_used = True 

644 self.transport._activate_outbound() 

645 

646 def _parse_kexgss_error(self, m): 

647 """ 

648 Parse the SSH2_MSG_KEXGSS_ERROR message (client mode). 

649 The server may send a GSS-API error message. if it does, we display 

650 the error by throwing an exception (client mode). 

651 

652 :param `Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message 

653 :raise SSHException: Contains GSS-API major and minor status as well as 

654 the error message and the language tag of the 

655 message 

656 """ 

657 maj_status = m.get_int() 

658 min_status = m.get_int() 

659 err_msg = m.get_string() 

660 m.get_string() # we don't care about the language (lang_tag)! 

661 raise SSHException( 

662 """GSS-API Error: 

663Major Status: {} 

664Minor Status: {} 

665Error Message: {} 

666""".format( 

667 maj_status, min_status, err_msg 

668 ) 

669 ) 

670 

671 

672class NullHostKey: 

673 """ 

674 This class represents the Null Host Key for GSS-API Key Exchange as defined 

675 in `RFC 4462 Section 5 

676 <https://tools.ietf.org/html/rfc4462.html#section-5>`_ 

677 """ 

678 

679 def __init__(self): 

680 self.key = "" 

681 

682 def __str__(self): 

683 return self.key 

684 

685 def get_name(self): 

686 return self.key