Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/ssh_gss.py: 21%

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

256 statements  

1# Copyright (C) 2013-2014 science + computing ag 

2# Author: Sebastian Deiss <sebastian.deiss@t-online.de> 

3# 

4# 

5# This file is part of paramiko. 

6# 

7# Paramiko is free software; you can redistribute it and/or modify it under the 

8# terms of the GNU Lesser General Public License as published by the Free 

9# Software Foundation; either version 2.1 of the License, or (at your option) 

10# any later version. 

11# 

12# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 

13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

14# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 

15# details. 

16# 

17# You should have received a copy of the GNU Lesser General Public License 

18# along with Paramiko; if not, write to the Free Software Foundation, Inc., 

19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

20 

21 

22""" 

23This module provides GSS-API / SSPI authentication as defined in :rfc:`4462`. 

24 

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

26 

27.. seealso:: :doc:`/api/kex_gss` 

28 

29.. versionadded:: 1.15 

30""" 

31 

32import struct 

33import os 

34import sys 

35 

36 

37#: A boolean constraint that indicates if GSS-API / SSPI is available. 

38GSS_AUTH_AVAILABLE = True 

39 

40 

41#: A tuple of the exception types used by the underlying GSSAPI implementation. 

42GSS_EXCEPTIONS = () 

43 

44 

45#: :var str _API: Constraint for the used API 

46_API = None 

47 

48try: 

49 import gssapi 

50 

51 if hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi": 

52 # old, unmaintained python-gssapi package 

53 _API = "MIT" # keep this for compatibility 

54 GSS_EXCEPTIONS = (gssapi.GSSException,) 

55 else: 

56 _API = "PYTHON-GSSAPI-NEW" 

57 GSS_EXCEPTIONS = ( 

58 gssapi.exceptions.GeneralError, 

59 gssapi.raw.misc.GSSError, 

60 ) 

61except (ImportError, OSError): 

62 try: 

63 import pywintypes 

64 import sspicon 

65 import sspi 

66 

67 _API = "SSPI" 

68 GSS_EXCEPTIONS = (pywintypes.error,) 

69 except ImportError: 

70 GSS_AUTH_AVAILABLE = False 

71 _API = None 

72 

73from paramiko.common import MSG_USERAUTH_REQUEST 

74from paramiko.ssh_exception import SSHException 

75from paramiko._version import __version_info__ 

76 

77 

78def GSSAuth(auth_method, gss_deleg_creds=True): 

79 """ 

80 Provide SSH2 GSS-API / SSPI authentication. 

81 

82 :param str auth_method: The name of the SSH authentication mechanism 

83 (gssapi-with-mic or gss-keyex) 

84 :param bool gss_deleg_creds: Delegate client credentials or not. 

85 We delegate credentials by default. 

86 :return: Either an `._SSH_GSSAPI_OLD` or `._SSH_GSSAPI_NEW` (Unix) 

87 object or an `_SSH_SSPI` (Windows) object 

88 :rtype: object 

89 

90 :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported. 

91 

92 :see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_ 

93 :note: Check for the available API and return either an `._SSH_GSSAPI_OLD` 

94 (MIT GSSAPI using python-gssapi package) object, an 

95 `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object 

96 or an `._SSH_SSPI` (MS SSPI) object. 

97 If there is no supported API available, 

98 ``None`` will be returned. 

99 """ 

100 if _API == "MIT": 

101 return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds) 

102 elif _API == "PYTHON-GSSAPI-NEW": 

103 return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds) 

104 elif _API == "SSPI" and os.name == "nt": 

105 return _SSH_SSPI(auth_method, gss_deleg_creds) 

106 else: 

107 raise ImportError("Unable to import a GSS-API / SSPI module!") 

108 

109 

110class _SSH_GSSAuth: 

111 """ 

112 Contains the shared variables and methods of `._SSH_GSSAPI_OLD`, 

113 `._SSH_GSSAPI_NEW` and `._SSH_SSPI`. 

114 """ 

115 

116 def __init__(self, auth_method, gss_deleg_creds): 

117 """ 

118 :param str auth_method: The name of the SSH authentication mechanism 

119 (gssapi-with-mic or gss-keyex) 

120 :param bool gss_deleg_creds: Delegate client credentials or not 

121 """ 

122 self._auth_method = auth_method 

123 self._gss_deleg_creds = gss_deleg_creds 

124 self._gss_host = None 

125 self._username = None 

126 self._session_id = None 

127 self._service = "ssh-connection" 

128 """ 

129 OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication, 

130 so we also support the krb5 mechanism only. 

131 """ 

132 self._krb5_mech = "1.2.840.113554.1.2.2" 

133 

134 # client mode 

135 self._gss_ctxt = None 

136 self._gss_ctxt_status = False 

137 

138 # server mode 

139 self._gss_srv_ctxt = None 

140 self._gss_srv_ctxt_status = False 

141 self.cc_file = None 

142 

143 def set_service(self, service): 

144 """ 

145 This is just a setter to use a non default service. 

146 I added this method, because RFC 4462 doesn't specify "ssh-connection" 

147 as the only service value. 

148 

149 :param str service: The desired SSH service 

150 """ 

151 if service.find("ssh-"): 

152 self._service = service 

153 

154 def set_username(self, username): 

155 """ 

156 Setter for C{username}. If GSS-API Key Exchange is performed, the 

157 username is not set by C{ssh_init_sec_context}. 

158 

159 :param str username: The name of the user who attempts to login 

160 """ 

161 self._username = username 

162 

163 def ssh_gss_oids(self, mode="client"): 

164 """ 

165 This method returns a single OID, because we only support the 

166 Kerberos V5 mechanism. 

167 

168 :param str mode: Client for client mode and server for server mode 

169 :return: A byte sequence containing the number of supported 

170 OIDs, the length of the OID and the actual OID encoded with 

171 DER 

172 :note: In server mode we just return the OID length and the DER encoded 

173 OID. 

174 """ 

175 from pyasn1.type.univ import ObjectIdentifier 

176 from pyasn1.codec.der import encoder 

177 

178 OIDs = self._make_uint32(1) 

179 krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech)) 

180 OID_len = self._make_uint32(len(krb5_OID)) 

181 if mode == "server": 

182 return OID_len + krb5_OID 

183 return OIDs + OID_len + krb5_OID 

184 

185 def ssh_check_mech(self, desired_mech): 

186 """ 

187 Check if the given OID is the Kerberos V5 OID (server mode). 

188 

189 :param str desired_mech: The desired GSS-API mechanism of the client 

190 :return: ``True`` if the given OID is supported, otherwise C{False} 

191 """ 

192 from pyasn1.codec.der import decoder 

193 

194 mech, __ = decoder.decode(desired_mech) 

195 if mech.__str__() != self._krb5_mech: 

196 return False 

197 return True 

198 

199 # Internals 

200 # ------------------------------------------------------------------------- 

201 def _make_uint32(self, integer): 

202 """ 

203 Create a 32 bit unsigned integer (The byte sequence of an integer). 

204 

205 :param int integer: The integer value to convert 

206 :return: The byte sequence of an 32 bit integer 

207 """ 

208 return struct.pack("!I", integer) 

209 

210 def _ssh_build_mic(self, session_id, username, service, auth_method): 

211 """ 

212 Create the SSH2 MIC filed for gssapi-with-mic. 

213 

214 :param str session_id: The SSH session ID 

215 :param str username: The name of the user who attempts to login 

216 :param str service: The requested SSH service 

217 :param str auth_method: The requested SSH authentication mechanism 

218 :return: The MIC as defined in RFC 4462. The contents of the 

219 MIC field are: 

220 string session_identifier, 

221 byte SSH_MSG_USERAUTH_REQUEST, 

222 string user-name, 

223 string service (ssh-connection), 

224 string authentication-method 

225 (gssapi-with-mic or gssapi-keyex) 

226 """ 

227 mic = self._make_uint32(len(session_id)) 

228 mic += session_id 

229 mic += struct.pack("B", MSG_USERAUTH_REQUEST) 

230 mic += self._make_uint32(len(username)) 

231 mic += username.encode() 

232 mic += self._make_uint32(len(service)) 

233 mic += service.encode() 

234 mic += self._make_uint32(len(auth_method)) 

235 mic += auth_method.encode() 

236 return mic 

237 

238 

239class _SSH_GSSAPI_OLD(_SSH_GSSAuth): 

240 """ 

241 Implementation of the GSS-API MIT Kerberos Authentication for SSH2, 

242 using the older (unmaintained) python-gssapi package. 

243 

244 :see: `.GSSAuth` 

245 """ 

246 

247 def __init__(self, auth_method, gss_deleg_creds): 

248 """ 

249 :param str auth_method: The name of the SSH authentication mechanism 

250 (gssapi-with-mic or gss-keyex) 

251 :param bool gss_deleg_creds: Delegate client credentials or not 

252 """ 

253 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

254 

255 if self._gss_deleg_creds: 

256 self._gss_flags = ( 

257 gssapi.C_PROT_READY_FLAG, 

258 gssapi.C_INTEG_FLAG, 

259 gssapi.C_MUTUAL_FLAG, 

260 gssapi.C_DELEG_FLAG, 

261 ) 

262 else: 

263 self._gss_flags = ( 

264 gssapi.C_PROT_READY_FLAG, 

265 gssapi.C_INTEG_FLAG, 

266 gssapi.C_MUTUAL_FLAG, 

267 ) 

268 

269 def ssh_init_sec_context( 

270 self, target, desired_mech=None, username=None, recv_token=None 

271 ): 

272 """ 

273 Initialize a GSS-API context. 

274 

275 :param str username: The name of the user who attempts to login 

276 :param str target: The hostname of the target to connect to 

277 :param str desired_mech: The negotiated GSS-API mechanism 

278 ("pseudo negotiated" mechanism, because we 

279 support just the krb5 mechanism :-)) 

280 :param str recv_token: The GSS-API token received from the Server 

281 :raises: 

282 `.SSHException` -- Is raised if the desired mechanism of the client 

283 is not supported 

284 :return: A ``String`` if the GSS-API has returned a token or 

285 ``None`` if no token was returned 

286 """ 

287 from pyasn1.codec.der import decoder 

288 

289 self._username = username 

290 self._gss_host = target 

291 targ_name = gssapi.Name( 

292 "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE 

293 ) 

294 ctx = gssapi.Context() 

295 ctx.flags = self._gss_flags 

296 if desired_mech is None: 

297 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech) 

298 else: 

299 mech, __ = decoder.decode(desired_mech) 

300 if mech.__str__() != self._krb5_mech: 

301 raise SSHException("Unsupported mechanism OID.") 

302 else: 

303 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech) 

304 token = None 

305 try: 

306 if recv_token is None: 

307 self._gss_ctxt = gssapi.InitContext( 

308 peer_name=targ_name, 

309 mech_type=krb5_mech, 

310 req_flags=ctx.flags, 

311 ) 

312 token = self._gss_ctxt.step(token) 

313 else: 

314 token = self._gss_ctxt.step(recv_token) 

315 except gssapi.GSSException: 

316 message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host) 

317 raise gssapi.GSSException(message) 

318 self._gss_ctxt_status = self._gss_ctxt.established 

319 return token 

320 

321 def ssh_get_mic(self, session_id, gss_kex=False): 

322 """ 

323 Create the MIC token for a SSH2 message. 

324 

325 :param str session_id: The SSH session ID 

326 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not 

327 :return: gssapi-with-mic: 

328 Returns the MIC token from GSS-API for the message we created 

329 with ``_ssh_build_mic``. 

330 gssapi-keyex: 

331 Returns the MIC token from GSS-API with the SSH session ID as 

332 message. 

333 """ 

334 self._session_id = session_id 

335 if not gss_kex: 

336 mic_field = self._ssh_build_mic( 

337 self._session_id, 

338 self._username, 

339 self._service, 

340 self._auth_method, 

341 ) 

342 mic_token = self._gss_ctxt.get_mic(mic_field) 

343 else: 

344 # for key exchange with gssapi-keyex 

345 mic_token = self._gss_srv_ctxt.get_mic(self._session_id) 

346 return mic_token 

347 

348 def ssh_accept_sec_context(self, hostname, recv_token, username=None): 

349 """ 

350 Accept a GSS-API context (server mode). 

351 

352 :param str hostname: The servers hostname 

353 :param str username: The name of the user who attempts to login 

354 :param str recv_token: The GSS-API Token received from the server, 

355 if it's not the initial call. 

356 :return: A ``String`` if the GSS-API has returned a token or ``None`` 

357 if no token was returned 

358 """ 

359 # hostname and username are not required for GSSAPI, but for SSPI 

360 self._gss_host = hostname 

361 self._username = username 

362 if self._gss_srv_ctxt is None: 

363 self._gss_srv_ctxt = gssapi.AcceptContext() 

364 token = self._gss_srv_ctxt.step(recv_token) 

365 self._gss_srv_ctxt_status = self._gss_srv_ctxt.established 

366 return token 

367 

368 def ssh_check_mic(self, mic_token, session_id, username=None): 

369 """ 

370 Verify the MIC token for a SSH2 message. 

371 

372 :param str mic_token: The MIC token received from the client 

373 :param str session_id: The SSH session ID 

374 :param str username: The name of the user who attempts to login 

375 :return: None if the MIC check was successful 

376 :raises: ``gssapi.GSSException`` -- if the MIC check failed 

377 """ 

378 self._session_id = session_id 

379 self._username = username 

380 if self._username is not None: 

381 # server mode 

382 mic_field = self._ssh_build_mic( 

383 self._session_id, 

384 self._username, 

385 self._service, 

386 self._auth_method, 

387 ) 

388 self._gss_srv_ctxt.verify_mic(mic_field, mic_token) 

389 else: 

390 # for key exchange with gssapi-keyex 

391 # client mode 

392 self._gss_ctxt.verify_mic(self._session_id, mic_token) 

393 

394 @property 

395 def credentials_delegated(self): 

396 """ 

397 Checks if credentials are delegated (server mode). 

398 

399 :return: ``True`` if credentials are delegated, otherwise ``False`` 

400 """ 

401 if self._gss_srv_ctxt.delegated_cred is not None: 

402 return True 

403 return False 

404 

405 def save_client_creds(self, client_token): 

406 """ 

407 Save the Client token in a file. This is used by the SSH server 

408 to store the client credentials if credentials are delegated 

409 (server mode). 

410 

411 :param str client_token: The GSS-API token received form the client 

412 :raises: 

413 ``NotImplementedError`` -- Credential delegation is currently not 

414 supported in server mode 

415 """ 

416 raise NotImplementedError 

417 

418 

419if __version_info__ < (2, 5): 

420 # provide the old name for strict backward compatibility 

421 _SSH_GSSAPI = _SSH_GSSAPI_OLD 

422 

423 

424class _SSH_GSSAPI_NEW(_SSH_GSSAuth): 

425 """ 

426 Implementation of the GSS-API MIT Kerberos Authentication for SSH2, 

427 using the newer, currently maintained gssapi package. 

428 

429 :see: `.GSSAuth` 

430 """ 

431 

432 def __init__(self, auth_method, gss_deleg_creds): 

433 """ 

434 :param str auth_method: The name of the SSH authentication mechanism 

435 (gssapi-with-mic or gss-keyex) 

436 :param bool gss_deleg_creds: Delegate client credentials or not 

437 """ 

438 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

439 

440 if self._gss_deleg_creds: 

441 self._gss_flags = ( 

442 gssapi.RequirementFlag.protection_ready, 

443 gssapi.RequirementFlag.integrity, 

444 gssapi.RequirementFlag.mutual_authentication, 

445 gssapi.RequirementFlag.delegate_to_peer, 

446 ) 

447 else: 

448 self._gss_flags = ( 

449 gssapi.RequirementFlag.protection_ready, 

450 gssapi.RequirementFlag.integrity, 

451 gssapi.RequirementFlag.mutual_authentication, 

452 ) 

453 

454 def ssh_init_sec_context( 

455 self, target, desired_mech=None, username=None, recv_token=None 

456 ): 

457 """ 

458 Initialize a GSS-API context. 

459 

460 :param str username: The name of the user who attempts to login 

461 :param str target: The hostname of the target to connect to 

462 :param str desired_mech: The negotiated GSS-API mechanism 

463 ("pseudo negotiated" mechanism, because we 

464 support just the krb5 mechanism :-)) 

465 :param str recv_token: The GSS-API token received from the Server 

466 :raises: `.SSHException` -- Is raised if the desired mechanism of the 

467 client is not supported 

468 :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled 

469 by the GSS-API implementation 

470 :return: A ``String`` if the GSS-API has returned a token or ``None`` 

471 if no token was returned 

472 """ 

473 from pyasn1.codec.der import decoder 

474 

475 self._username = username 

476 self._gss_host = target 

477 targ_name = gssapi.Name( 

478 "host@" + self._gss_host, 

479 name_type=gssapi.NameType.hostbased_service, 

480 ) 

481 if desired_mech is not None: 

482 mech, __ = decoder.decode(desired_mech) 

483 if mech.__str__() != self._krb5_mech: 

484 raise SSHException("Unsupported mechanism OID.") 

485 krb5_mech = gssapi.MechType.kerberos 

486 token = None 

487 if recv_token is None: 

488 self._gss_ctxt = gssapi.SecurityContext( 

489 name=targ_name, 

490 flags=self._gss_flags, 

491 mech=krb5_mech, 

492 usage="initiate", 

493 ) 

494 token = self._gss_ctxt.step(token) 

495 else: 

496 token = self._gss_ctxt.step(recv_token) 

497 self._gss_ctxt_status = self._gss_ctxt.complete 

498 return token 

499 

500 def ssh_get_mic(self, session_id, gss_kex=False): 

501 """ 

502 Create the MIC token for a SSH2 message. 

503 

504 :param str session_id: The SSH session ID 

505 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not 

506 :return: gssapi-with-mic: 

507 Returns the MIC token from GSS-API for the message we created 

508 with ``_ssh_build_mic``. 

509 gssapi-keyex: 

510 Returns the MIC token from GSS-API with the SSH session ID as 

511 message. 

512 :rtype: str 

513 """ 

514 self._session_id = session_id 

515 if not gss_kex: 

516 mic_field = self._ssh_build_mic( 

517 self._session_id, 

518 self._username, 

519 self._service, 

520 self._auth_method, 

521 ) 

522 mic_token = self._gss_ctxt.get_signature(mic_field) 

523 else: 

524 # for key exchange with gssapi-keyex 

525 mic_token = self._gss_srv_ctxt.get_signature(self._session_id) 

526 return mic_token 

527 

528 def ssh_accept_sec_context(self, hostname, recv_token, username=None): 

529 """ 

530 Accept a GSS-API context (server mode). 

531 

532 :param str hostname: The servers hostname 

533 :param str username: The name of the user who attempts to login 

534 :param str recv_token: The GSS-API Token received from the server, 

535 if it's not the initial call. 

536 :return: A ``String`` if the GSS-API has returned a token or ``None`` 

537 if no token was returned 

538 """ 

539 # hostname and username are not required for GSSAPI, but for SSPI 

540 self._gss_host = hostname 

541 self._username = username 

542 if self._gss_srv_ctxt is None: 

543 self._gss_srv_ctxt = gssapi.SecurityContext(usage="accept") 

544 token = self._gss_srv_ctxt.step(recv_token) 

545 self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete 

546 return token 

547 

548 def ssh_check_mic(self, mic_token, session_id, username=None): 

549 """ 

550 Verify the MIC token for a SSH2 message. 

551 

552 :param str mic_token: The MIC token received from the client 

553 :param str session_id: The SSH session ID 

554 :param str username: The name of the user who attempts to login 

555 :return: None if the MIC check was successful 

556 :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed 

557 """ 

558 self._session_id = session_id 

559 self._username = username 

560 if self._username is not None: 

561 # server mode 

562 mic_field = self._ssh_build_mic( 

563 self._session_id, 

564 self._username, 

565 self._service, 

566 self._auth_method, 

567 ) 

568 self._gss_srv_ctxt.verify_signature(mic_field, mic_token) 

569 else: 

570 # for key exchange with gssapi-keyex 

571 # client mode 

572 self._gss_ctxt.verify_signature(self._session_id, mic_token) 

573 

574 @property 

575 def credentials_delegated(self): 

576 """ 

577 Checks if credentials are delegated (server mode). 

578 

579 :return: ``True`` if credentials are delegated, otherwise ``False`` 

580 :rtype: bool 

581 """ 

582 if self._gss_srv_ctxt.delegated_creds is not None: 

583 return True 

584 return False 

585 

586 def save_client_creds(self, client_token): 

587 """ 

588 Save the Client token in a file. This is used by the SSH server 

589 to store the client credentials if credentials are delegated 

590 (server mode). 

591 

592 :param str client_token: The GSS-API token received form the client 

593 :raises: ``NotImplementedError`` -- Credential delegation is currently 

594 not supported in server mode 

595 """ 

596 raise NotImplementedError 

597 

598 

599class _SSH_SSPI(_SSH_GSSAuth): 

600 """ 

601 Implementation of the Microsoft SSPI Kerberos Authentication for SSH2. 

602 

603 :see: `.GSSAuth` 

604 """ 

605 

606 def __init__(self, auth_method, gss_deleg_creds): 

607 """ 

608 :param str auth_method: The name of the SSH authentication mechanism 

609 (gssapi-with-mic or gss-keyex) 

610 :param bool gss_deleg_creds: Delegate client credentials or not 

611 """ 

612 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

613 

614 if self._gss_deleg_creds: 

615 self._gss_flags = ( 

616 sspicon.ISC_REQ_INTEGRITY 

617 | sspicon.ISC_REQ_MUTUAL_AUTH 

618 | sspicon.ISC_REQ_DELEGATE 

619 ) 

620 else: 

621 self._gss_flags = ( 

622 sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH 

623 ) 

624 

625 def ssh_init_sec_context( 

626 self, target, desired_mech=None, username=None, recv_token=None 

627 ): 

628 """ 

629 Initialize a SSPI context. 

630 

631 :param str username: The name of the user who attempts to login 

632 :param str target: The FQDN of the target to connect to 

633 :param str desired_mech: The negotiated SSPI mechanism 

634 ("pseudo negotiated" mechanism, because we 

635 support just the krb5 mechanism :-)) 

636 :param recv_token: The SSPI token received from the Server 

637 :raises: 

638 `.SSHException` -- Is raised if the desired mechanism of the client 

639 is not supported 

640 :return: A ``String`` if the SSPI has returned a token or ``None`` if 

641 no token was returned 

642 """ 

643 from pyasn1.codec.der import decoder 

644 

645 self._username = username 

646 self._gss_host = target 

647 error = 0 

648 targ_name = "host/" + self._gss_host 

649 if desired_mech is not None: 

650 mech, __ = decoder.decode(desired_mech) 

651 if mech.__str__() != self._krb5_mech: 

652 raise SSHException("Unsupported mechanism OID.") 

653 try: 

654 if recv_token is None: 

655 self._gss_ctxt = sspi.ClientAuth( 

656 "Kerberos", scflags=self._gss_flags, targetspn=targ_name 

657 ) 

658 error, token = self._gss_ctxt.authorize(recv_token) 

659 token = token[0].Buffer 

660 except pywintypes.error as e: 

661 e.strerror += ", Target: {}".format(self._gss_host) 

662 raise 

663 

664 if error == 0: 

665 """ 

666 if the status is GSS_COMPLETE (error = 0) the context is fully 

667 established an we can set _gss_ctxt_status to True. 

668 """ 

669 self._gss_ctxt_status = True 

670 token = None 

671 """ 

672 You won't get another token if the context is fully established, 

673 so i set token to None instead of "" 

674 """ 

675 return token 

676 

677 def ssh_get_mic(self, session_id, gss_kex=False): 

678 """ 

679 Create the MIC token for a SSH2 message. 

680 

681 :param str session_id: The SSH session ID 

682 :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not 

683 :return: gssapi-with-mic: 

684 Returns the MIC token from SSPI for the message we created 

685 with ``_ssh_build_mic``. 

686 gssapi-keyex: 

687 Returns the MIC token from SSPI with the SSH session ID as 

688 message. 

689 """ 

690 self._session_id = session_id 

691 if not gss_kex: 

692 mic_field = self._ssh_build_mic( 

693 self._session_id, 

694 self._username, 

695 self._service, 

696 self._auth_method, 

697 ) 

698 mic_token = self._gss_ctxt.sign(mic_field) 

699 else: 

700 # for key exchange with gssapi-keyex 

701 mic_token = self._gss_srv_ctxt.sign(self._session_id) 

702 return mic_token 

703 

704 def ssh_accept_sec_context(self, hostname, username, recv_token): 

705 """ 

706 Accept a SSPI context (server mode). 

707 

708 :param str hostname: The servers FQDN 

709 :param str username: The name of the user who attempts to login 

710 :param str recv_token: The SSPI Token received from the server, 

711 if it's not the initial call. 

712 :return: A ``String`` if the SSPI has returned a token or ``None`` if 

713 no token was returned 

714 """ 

715 self._gss_host = hostname 

716 self._username = username 

717 targ_name = "host/" + self._gss_host 

718 self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name) 

719 error, token = self._gss_srv_ctxt.authorize(recv_token) 

720 token = token[0].Buffer 

721 if error == 0: 

722 self._gss_srv_ctxt_status = True 

723 token = None 

724 return token 

725 

726 def ssh_check_mic(self, mic_token, session_id, username=None): 

727 """ 

728 Verify the MIC token for a SSH2 message. 

729 

730 :param str mic_token: The MIC token received from the client 

731 :param str session_id: The SSH session ID 

732 :param str username: The name of the user who attempts to login 

733 :return: None if the MIC check was successful 

734 :raises: ``sspi.error`` -- if the MIC check failed 

735 """ 

736 self._session_id = session_id 

737 self._username = username 

738 if username is not None: 

739 # server mode 

740 mic_field = self._ssh_build_mic( 

741 self._session_id, 

742 self._username, 

743 self._service, 

744 self._auth_method, 

745 ) 

746 # Verifies data and its signature. If verification fails, an 

747 # sspi.error will be raised. 

748 self._gss_srv_ctxt.verify(mic_field, mic_token) 

749 else: 

750 # for key exchange with gssapi-keyex 

751 # client mode 

752 # Verifies data and its signature. If verification fails, an 

753 # sspi.error will be raised. 

754 self._gss_ctxt.verify(self._session_id, mic_token) 

755 

756 @property 

757 def credentials_delegated(self): 

758 """ 

759 Checks if credentials are delegated (server mode). 

760 

761 :return: ``True`` if credentials are delegated, otherwise ``False`` 

762 """ 

763 return self._gss_flags & sspicon.ISC_REQ_DELEGATE and ( 

764 self._gss_srv_ctxt_status or self._gss_flags 

765 ) 

766 

767 def save_client_creds(self, client_token): 

768 """ 

769 Save the Client token in a file. This is used by the SSH server 

770 to store the client credentails if credentials are delegated 

771 (server mode). 

772 

773 :param str client_token: The SSPI token received form the client 

774 :raises: 

775 ``NotImplementedError`` -- Credential delegation is currently not 

776 supported in server mode 

777 """ 

778 raise NotImplementedError