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

253 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 

75 

76 

77def GSSAuth(auth_method, gss_deleg_creds=True): 

78 """ 

79 Provide SSH2 GSS-API / SSPI authentication. 

80 

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

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

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

84 We delegate credentials by default. 

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

86 object or an `_SSH_SSPI` (Windows) object 

87 :rtype: object 

88 

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

90 

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

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

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

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

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

96 If there is no supported API available, 

97 ``None`` will be returned. 

98 """ 

99 if _API == "MIT": 

100 return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds) 

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

102 return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds) 

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

104 return _SSH_SSPI(auth_method, gss_deleg_creds) 

105 else: 

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

107 

108 

109class _SSH_GSSAuth: 

110 """ 

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

112 `._SSH_GSSAPI_NEW` and `._SSH_SSPI`. 

113 """ 

114 

115 def __init__(self, auth_method, gss_deleg_creds): 

116 """ 

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

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

119 :param bool gss_deleg_creds: Delegate client credentials or not 

120 """ 

121 self._auth_method = auth_method 

122 self._gss_deleg_creds = gss_deleg_creds 

123 self._gss_host = None 

124 self._username = None 

125 self._session_id = None 

126 self._service = "ssh-connection" 

127 """ 

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

129 so we also support the krb5 mechanism only. 

130 """ 

131 self._krb5_mech = "1.2.840.113554.1.2.2" 

132 

133 # client mode 

134 self._gss_ctxt = None 

135 self._gss_ctxt_status = False 

136 

137 # server mode 

138 self._gss_srv_ctxt = None 

139 self._gss_srv_ctxt_status = False 

140 self.cc_file = None 

141 

142 def set_service(self, service): 

143 """ 

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

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

146 as the only service value. 

147 

148 :param str service: The desired SSH service 

149 """ 

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

151 self._service = service 

152 

153 def set_username(self, username): 

154 """ 

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

156 username is not set by C{ssh_init_sec_context}. 

157 

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

159 """ 

160 self._username = username 

161 

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

163 """ 

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

165 Kerberos V5 mechanism. 

166 

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

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

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

170 DER 

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

172 OID. 

173 """ 

174 from pyasn1.type.univ import ObjectIdentifier 

175 from pyasn1.codec.der import encoder 

176 

177 OIDs = self._make_uint32(1) 

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

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

180 if mode == "server": 

181 return OID_len + krb5_OID 

182 return OIDs + OID_len + krb5_OID 

183 

184 def ssh_check_mech(self, desired_mech): 

185 """ 

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

187 

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

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

190 """ 

191 from pyasn1.codec.der import decoder 

192 

193 mech, __ = decoder.decode(desired_mech) 

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

195 return False 

196 return True 

197 

198 # Internals 

199 # ------------------------------------------------------------------------- 

200 def _make_uint32(self, integer): 

201 """ 

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

203 

204 :param int integer: The integer value to convert 

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

206 """ 

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

208 

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

210 """ 

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

212 

213 :param str session_id: The SSH session ID 

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

215 :param str service: The requested SSH service 

216 :param str auth_method: The requested SSH authentication mechanism 

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

218 MIC field are: 

219 string session_identifier, 

220 byte SSH_MSG_USERAUTH_REQUEST, 

221 string user-name, 

222 string service (ssh-connection), 

223 string authentication-method 

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

225 """ 

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

227 mic += session_id 

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

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

230 mic += username.encode() 

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

232 mic += service.encode() 

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

234 mic += auth_method.encode() 

235 return mic 

236 

237 

238class _SSH_GSSAPI_OLD(_SSH_GSSAuth): 

239 """ 

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

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

242 

243 :see: `.GSSAuth` 

244 """ 

245 

246 def __init__(self, auth_method, gss_deleg_creds): 

247 """ 

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

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

250 :param bool gss_deleg_creds: Delegate client credentials or not 

251 """ 

252 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

253 

254 if self._gss_deleg_creds: 

255 self._gss_flags = ( 

256 gssapi.C_PROT_READY_FLAG, 

257 gssapi.C_INTEG_FLAG, 

258 gssapi.C_MUTUAL_FLAG, 

259 gssapi.C_DELEG_FLAG, 

260 ) 

261 else: 

262 self._gss_flags = ( 

263 gssapi.C_PROT_READY_FLAG, 

264 gssapi.C_INTEG_FLAG, 

265 gssapi.C_MUTUAL_FLAG, 

266 ) 

267 

268 def ssh_init_sec_context( 

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

270 ): 

271 """ 

272 Initialize a GSS-API context. 

273 

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

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

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

277 ("pseudo negotiated" mechanism, because we 

278 support just the krb5 mechanism :-)) 

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

280 :raises: 

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

282 is not supported 

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

284 ``None`` if no token was returned 

285 """ 

286 from pyasn1.codec.der import decoder 

287 

288 self._username = username 

289 self._gss_host = target 

290 targ_name = gssapi.Name( 

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

292 ) 

293 ctx = gssapi.Context() 

294 ctx.flags = self._gss_flags 

295 if desired_mech is None: 

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

297 else: 

298 mech, __ = decoder.decode(desired_mech) 

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

300 raise SSHException("Unsupported mechanism OID.") 

301 else: 

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

303 token = None 

304 try: 

305 if recv_token is None: 

306 self._gss_ctxt = gssapi.InitContext( 

307 peer_name=targ_name, 

308 mech_type=krb5_mech, 

309 req_flags=ctx.flags, 

310 ) 

311 token = self._gss_ctxt.step(token) 

312 else: 

313 token = self._gss_ctxt.step(recv_token) 

314 except gssapi.GSSException: 

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

316 raise gssapi.GSSException(message) 

317 self._gss_ctxt_status = self._gss_ctxt.established 

318 return token 

319 

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

321 """ 

322 Create the MIC token for a SSH2 message. 

323 

324 :param str session_id: The SSH session ID 

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

326 :return: gssapi-with-mic: 

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

328 with ``_ssh_build_mic``. 

329 gssapi-keyex: 

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

331 message. 

332 """ 

333 self._session_id = session_id 

334 if not gss_kex: 

335 mic_field = self._ssh_build_mic( 

336 self._session_id, 

337 self._username, 

338 self._service, 

339 self._auth_method, 

340 ) 

341 mic_token = self._gss_ctxt.get_mic(mic_field) 

342 else: 

343 # for key exchange with gssapi-keyex 

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

345 return mic_token 

346 

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

348 """ 

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

350 

351 :param str hostname: The servers hostname 

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

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

354 if it's not the initial call. 

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

356 if no token was returned 

357 """ 

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

359 self._gss_host = hostname 

360 self._username = username 

361 if self._gss_srv_ctxt is None: 

362 self._gss_srv_ctxt = gssapi.AcceptContext() 

363 token = self._gss_srv_ctxt.step(recv_token) 

364 self._gss_srv_ctxt_status = self._gss_srv_ctxt.established 

365 return token 

366 

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

368 """ 

369 Verify the MIC token for a SSH2 message. 

370 

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

372 :param str session_id: The SSH session ID 

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

374 :return: None if the MIC check was successful 

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

376 """ 

377 self._session_id = session_id 

378 self._username = username 

379 if self._username is not None: 

380 # server mode 

381 mic_field = self._ssh_build_mic( 

382 self._session_id, 

383 self._username, 

384 self._service, 

385 self._auth_method, 

386 ) 

387 self._gss_srv_ctxt.verify_mic(mic_field, mic_token) 

388 else: 

389 # for key exchange with gssapi-keyex 

390 # client mode 

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

392 

393 @property 

394 def credentials_delegated(self): 

395 """ 

396 Checks if credentials are delegated (server mode). 

397 

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

399 """ 

400 if self._gss_srv_ctxt.delegated_cred is not None: 

401 return True 

402 return False 

403 

404 def save_client_creds(self, client_token): 

405 """ 

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

407 to store the client credentials if credentials are delegated 

408 (server mode). 

409 

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

411 :raises: 

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

413 supported in server mode 

414 """ 

415 raise NotImplementedError 

416 

417 

418class _SSH_GSSAPI_NEW(_SSH_GSSAuth): 

419 """ 

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

421 using the newer, currently maintained gssapi package. 

422 

423 :see: `.GSSAuth` 

424 """ 

425 

426 def __init__(self, auth_method, gss_deleg_creds): 

427 """ 

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

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

430 :param bool gss_deleg_creds: Delegate client credentials or not 

431 """ 

432 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

433 

434 if self._gss_deleg_creds: 

435 self._gss_flags = ( 

436 gssapi.RequirementFlag.protection_ready, 

437 gssapi.RequirementFlag.integrity, 

438 gssapi.RequirementFlag.mutual_authentication, 

439 gssapi.RequirementFlag.delegate_to_peer, 

440 ) 

441 else: 

442 self._gss_flags = ( 

443 gssapi.RequirementFlag.protection_ready, 

444 gssapi.RequirementFlag.integrity, 

445 gssapi.RequirementFlag.mutual_authentication, 

446 ) 

447 

448 def ssh_init_sec_context( 

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

450 ): 

451 """ 

452 Initialize a GSS-API context. 

453 

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

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

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

457 ("pseudo negotiated" mechanism, because we 

458 support just the krb5 mechanism :-)) 

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

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

461 client is not supported 

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

463 by the GSS-API implementation 

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

465 if no token was returned 

466 """ 

467 from pyasn1.codec.der import decoder 

468 

469 self._username = username 

470 self._gss_host = target 

471 targ_name = gssapi.Name( 

472 "host@" + self._gss_host, 

473 name_type=gssapi.NameType.hostbased_service, 

474 ) 

475 if desired_mech is not None: 

476 mech, __ = decoder.decode(desired_mech) 

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

478 raise SSHException("Unsupported mechanism OID.") 

479 krb5_mech = gssapi.MechType.kerberos 

480 token = None 

481 if recv_token is None: 

482 self._gss_ctxt = gssapi.SecurityContext( 

483 name=targ_name, 

484 flags=self._gss_flags, 

485 mech=krb5_mech, 

486 usage="initiate", 

487 ) 

488 token = self._gss_ctxt.step(token) 

489 else: 

490 token = self._gss_ctxt.step(recv_token) 

491 self._gss_ctxt_status = self._gss_ctxt.complete 

492 return token 

493 

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

495 """ 

496 Create the MIC token for a SSH2 message. 

497 

498 :param str session_id: The SSH session ID 

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

500 :return: gssapi-with-mic: 

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

502 with ``_ssh_build_mic``. 

503 gssapi-keyex: 

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

505 message. 

506 :rtype: str 

507 """ 

508 self._session_id = session_id 

509 if not gss_kex: 

510 mic_field = self._ssh_build_mic( 

511 self._session_id, 

512 self._username, 

513 self._service, 

514 self._auth_method, 

515 ) 

516 mic_token = self._gss_ctxt.get_signature(mic_field) 

517 else: 

518 # for key exchange with gssapi-keyex 

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

520 return mic_token 

521 

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

523 """ 

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

525 

526 :param str hostname: The servers hostname 

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

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

529 if it's not the initial call. 

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

531 if no token was returned 

532 """ 

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

534 self._gss_host = hostname 

535 self._username = username 

536 if self._gss_srv_ctxt is None: 

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

538 token = self._gss_srv_ctxt.step(recv_token) 

539 self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete 

540 return token 

541 

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

543 """ 

544 Verify the MIC token for a SSH2 message. 

545 

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

547 :param str session_id: The SSH session ID 

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

549 :return: None if the MIC check was successful 

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

551 """ 

552 self._session_id = session_id 

553 self._username = username 

554 if self._username is not None: 

555 # server mode 

556 mic_field = self._ssh_build_mic( 

557 self._session_id, 

558 self._username, 

559 self._service, 

560 self._auth_method, 

561 ) 

562 self._gss_srv_ctxt.verify_signature(mic_field, mic_token) 

563 else: 

564 # for key exchange with gssapi-keyex 

565 # client mode 

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

567 

568 @property 

569 def credentials_delegated(self): 

570 """ 

571 Checks if credentials are delegated (server mode). 

572 

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

574 :rtype: bool 

575 """ 

576 if self._gss_srv_ctxt.delegated_creds is not None: 

577 return True 

578 return False 

579 

580 def save_client_creds(self, client_token): 

581 """ 

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

583 to store the client credentials if credentials are delegated 

584 (server mode). 

585 

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

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

588 not supported in server mode 

589 """ 

590 raise NotImplementedError 

591 

592 

593class _SSH_SSPI(_SSH_GSSAuth): 

594 """ 

595 Implementation of the Microsoft SSPI Kerberos Authentication for SSH2. 

596 

597 :see: `.GSSAuth` 

598 """ 

599 

600 def __init__(self, auth_method, gss_deleg_creds): 

601 """ 

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

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

604 :param bool gss_deleg_creds: Delegate client credentials or not 

605 """ 

606 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds) 

607 

608 if self._gss_deleg_creds: 

609 self._gss_flags = ( 

610 sspicon.ISC_REQ_INTEGRITY 

611 | sspicon.ISC_REQ_MUTUAL_AUTH 

612 | sspicon.ISC_REQ_DELEGATE 

613 ) 

614 else: 

615 self._gss_flags = ( 

616 sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH 

617 ) 

618 

619 def ssh_init_sec_context( 

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

621 ): 

622 """ 

623 Initialize a SSPI context. 

624 

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

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

627 :param str desired_mech: The negotiated SSPI mechanism 

628 ("pseudo negotiated" mechanism, because we 

629 support just the krb5 mechanism :-)) 

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

631 :raises: 

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

633 is not supported 

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

635 no token was returned 

636 """ 

637 from pyasn1.codec.der import decoder 

638 

639 self._username = username 

640 self._gss_host = target 

641 error = 0 

642 targ_name = "host/" + self._gss_host 

643 if desired_mech is not None: 

644 mech, __ = decoder.decode(desired_mech) 

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

646 raise SSHException("Unsupported mechanism OID.") 

647 try: 

648 if recv_token is None: 

649 self._gss_ctxt = sspi.ClientAuth( 

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

651 ) 

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

653 token = token[0].Buffer 

654 except pywintypes.error as e: 

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

656 raise 

657 

658 if error == 0: 

659 """ 

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

661 established an we can set _gss_ctxt_status to True. 

662 """ 

663 self._gss_ctxt_status = True 

664 token = None 

665 """ 

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

667 so i set token to None instead of "" 

668 """ 

669 return token 

670 

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

672 """ 

673 Create the MIC token for a SSH2 message. 

674 

675 :param str session_id: The SSH session ID 

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

677 :return: gssapi-with-mic: 

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

679 with ``_ssh_build_mic``. 

680 gssapi-keyex: 

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

682 message. 

683 """ 

684 self._session_id = session_id 

685 if not gss_kex: 

686 mic_field = self._ssh_build_mic( 

687 self._session_id, 

688 self._username, 

689 self._service, 

690 self._auth_method, 

691 ) 

692 mic_token = self._gss_ctxt.sign(mic_field) 

693 else: 

694 # for key exchange with gssapi-keyex 

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

696 return mic_token 

697 

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

699 """ 

700 Accept a SSPI context (server mode). 

701 

702 :param str hostname: The servers FQDN 

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

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

705 if it's not the initial call. 

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

707 no token was returned 

708 """ 

709 self._gss_host = hostname 

710 self._username = username 

711 targ_name = "host/" + self._gss_host 

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

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

714 token = token[0].Buffer 

715 if error == 0: 

716 self._gss_srv_ctxt_status = True 

717 token = None 

718 return token 

719 

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

721 """ 

722 Verify the MIC token for a SSH2 message. 

723 

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

725 :param str session_id: The SSH session ID 

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

727 :return: None if the MIC check was successful 

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

729 """ 

730 self._session_id = session_id 

731 self._username = username 

732 if username is not None: 

733 # server mode 

734 mic_field = self._ssh_build_mic( 

735 self._session_id, 

736 self._username, 

737 self._service, 

738 self._auth_method, 

739 ) 

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

741 # sspi.error will be raised. 

742 self._gss_srv_ctxt.verify(mic_field, mic_token) 

743 else: 

744 # for key exchange with gssapi-keyex 

745 # client mode 

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

747 # sspi.error will be raised. 

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

749 

750 @property 

751 def credentials_delegated(self): 

752 """ 

753 Checks if credentials are delegated (server mode). 

754 

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

756 """ 

757 return self._gss_flags & sspicon.ISC_REQ_DELEGATE and ( 

758 self._gss_srv_ctxt_status or self._gss_flags 

759 ) 

760 

761 def save_client_creds(self, client_token): 

762 """ 

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

764 to store the client credentials if credentials are delegated 

765 (server mode). 

766 

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

768 :raises: 

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

770 supported in server mode 

771 """ 

772 raise NotImplementedError