creds.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. from gssapi.raw import creds as rcreds
  2. from gssapi.raw import named_tuples as tuples
  3. from gssapi._utils import import_gssapi_extension, _encode_dict
  4. from gssapi import names
  5. rcred_imp_exp = import_gssapi_extension('cred_imp_exp')
  6. rcred_s4u = import_gssapi_extension('s4u')
  7. rcred_cred_store = import_gssapi_extension('cred_store')
  8. rcred_rfc5588 = import_gssapi_extension('rfc5588')
  9. class Credentials(rcreds.Creds):
  10. """GSSAPI Credentials
  11. This class represents a set of GSSAPI credentials which may
  12. be used with and/or returned by other GSSAPI methods.
  13. It inherits from the low-level GSSAPI :class:`~gssapi.raw.creds.Creds`
  14. class, and thus may used with both low-level and high-level API methods.
  15. If your implementation of GSSAPI supports the credentials import-export
  16. extension, you may pickle and unpickle this object.
  17. The constructor either acquires or imports a set of GSSAPI
  18. credentials.
  19. If the `base` argument is used, an existing
  20. :class:`~gssapi.raw.creds.Cred` object from the low-level API is
  21. converted into a high-level object.
  22. If the `token` argument is used, the credentials
  23. are imported using the token, if the credentials import-export
  24. extension is supported (:requires-ext:`cred_imp_exp`).
  25. Otherwise, the credentials are acquired as per the
  26. :meth:`acquire` method.
  27. Raises:
  28. BadMechanismError
  29. BadNameTypeError
  30. BadNameError
  31. ExpiredCredentialsError
  32. MissingCredentialsError
  33. """
  34. __slots__ = ()
  35. def __new__(cls, base=None, token=None, name=None, lifetime=None,
  36. mechs=None, usage='both', store=None):
  37. # TODO(directxman12): this is missing support for password
  38. # (non-RFC method)
  39. if base is not None:
  40. base_creds = base
  41. elif token is not None:
  42. if rcred_imp_exp is None:
  43. raise NotImplementedError("Your GSSAPI implementation does "
  44. "not have support for importing and "
  45. "exporting creditials")
  46. base_creds = rcred_imp_exp.import_cred(token)
  47. else:
  48. res = cls.acquire(name, lifetime, mechs, usage,
  49. store=store)
  50. base_creds = res.creds
  51. return super(Credentials, cls).__new__(cls, base_creds)
  52. @property
  53. def name(self):
  54. """Get the name associated with these credentials"""
  55. return self.inquire(name=True, lifetime=False,
  56. usage=False, mechs=False).name
  57. @property
  58. def lifetime(self):
  59. """Get the remaining lifetime of these credentials"""
  60. return self.inquire(name=False, lifetime=True,
  61. usage=False, mechs=False).lifetime
  62. @property
  63. def mechs(self):
  64. """Get the mechanisms for these credentials"""
  65. return self.inquire(name=False, lifetime=False,
  66. usage=False, mechs=True).mechs
  67. @property
  68. def usage(self):
  69. """Get the usage (initiate, accept, or both) of these credentials"""
  70. return self.inquire(name=False, lifetime=False,
  71. usage=True, mechs=False).usage
  72. @classmethod
  73. def acquire(cls, name=None, lifetime=None, mechs=None, usage='both',
  74. store=None):
  75. """Acquire GSSAPI credentials
  76. This method acquires credentials. If the `store` argument is
  77. used, the credentials will be acquired from the given
  78. credential store (if supported). Otherwise, the credentials are
  79. acquired from the default store.
  80. The credential store information is a dictionary containing
  81. mechanisms-specific keys and values pointing to a credential store
  82. or stores.
  83. Using a non-default store requires support for the credentials store
  84. extension.
  85. Args:
  86. name (Name): the name associated with the credentials,
  87. or None for the default name
  88. lifetime (int): the desired lifetime of the credentials, or None
  89. for indefinite
  90. mechs (list): the desired :class:`MechType` OIDs to be used
  91. with the credentials, or None for the default set
  92. usage (str): the usage for the credentials -- either 'both',
  93. 'initiate', or 'accept'
  94. store (dict): the credential store information pointing to the
  95. credential store from which to acquire the credentials,
  96. or None for the default store (:requires-ext:`cred_store`)
  97. Returns:
  98. AcquireCredResult: the acquired credentials and information about
  99. them
  100. Raises:
  101. BadMechanismError
  102. BadNameTypeError
  103. BadNameError
  104. ExpiredCredentialsError
  105. MissingCredentialsError
  106. """
  107. if store is None:
  108. res = rcreds.acquire_cred(name, lifetime,
  109. mechs, usage)
  110. else:
  111. if rcred_cred_store is None:
  112. raise NotImplementedError("Your GSSAPI implementation does "
  113. "not have support for manipulating "
  114. "credential stores")
  115. store = _encode_dict(store)
  116. res = rcred_cred_store.acquire_cred_from(store, name,
  117. lifetime, mechs,
  118. usage)
  119. return tuples.AcquireCredResult(cls(base=res.creds), res.mechs,
  120. res.lifetime)
  121. def store(self, store=None, usage='both', mech=None,
  122. overwrite=False, set_default=False):
  123. """Store these credentials into the given store
  124. This method stores the current credentials into the specified
  125. credentials store. If the default store is used, support for
  126. :rfc:`5588` is required. Otherwise, support for the credentials
  127. store extension is required.
  128. :requires-ext:`rfc5588` or :requires-ext:`cred_store`
  129. Args:
  130. store (dict): the store into which to store the credentials,
  131. or None for the default store.
  132. usage (str): the usage to store the credentials with -- either
  133. 'both', 'initiate', or 'accept'
  134. mech (OID): the :class:`MechType` to associate with the
  135. stored credentials
  136. overwrite (bool): whether or not to overwrite existing credentials
  137. stored with the same name, etc
  138. set_default (bool): whether or not to set these credentials as
  139. the default credentials for the given store.
  140. Returns:
  141. StoreCredResult: the results of the credential storing operation
  142. Raises:
  143. GSSError
  144. ExpiredCredentialsError
  145. MissingCredentialsError
  146. OperationUnavailableError
  147. DuplicateCredentialsElementError
  148. """
  149. if store is None:
  150. if rcred_rfc5588 is None:
  151. raise NotImplementedError("Your GSSAPI implementation does "
  152. "not have support for RFC 5588")
  153. return rcred_rfc5588.store_cred(self, usage, mech,
  154. overwrite, set_default)
  155. else:
  156. if rcred_cred_store is None:
  157. raise NotImplementedError("Your GSSAPI implementation does "
  158. "not have support for manipulating "
  159. "credential stores directly")
  160. store = _encode_dict(store)
  161. return rcred_cred_store.store_cred_into(store, self, usage, mech,
  162. overwrite, set_default)
  163. def impersonate(self, name=None, lifetime=None, mechs=None,
  164. usage='initiate'):
  165. """Impersonate a name using the current credentials
  166. This method acquires credentials by impersonating another
  167. name using the current credentials.
  168. :requires-ext:`s4u`
  169. Args:
  170. name (Name): the name to impersonate
  171. lifetime (int): the desired lifetime of the new credentials,
  172. or None for indefinite
  173. mechs (list): the desired :class:`MechType` OIDs for the new
  174. credentials
  175. usage (str): the desired usage for the new credentials -- either
  176. 'both', 'initiate', or 'accept'. Note that some mechanisms
  177. may only support 'initiate'.
  178. Returns:
  179. Credentials: the new credentials impersonating the given name
  180. """
  181. if rcred_s4u is None:
  182. raise NotImplementedError("Your GSSAPI implementation does not "
  183. "have support for S4U")
  184. res = rcred_s4u.acquire_cred_impersonate_name(self, name,
  185. lifetime, mechs,
  186. usage)
  187. return type(self)(base=res.creds)
  188. def inquire(self, name=True, lifetime=True, usage=True, mechs=True):
  189. """Inspect these credentials for information
  190. This method inspects these credentials for information about them.
  191. Args:
  192. name (bool): get the name associated with the credentials
  193. lifetime (bool): get the remaining lifetime for the credentials
  194. usage (bool): get the usage for the credentials
  195. mechs (bool): get the mechanisms associated with the credentials
  196. Returns:
  197. InquireCredResult: the information about the credentials,
  198. with None used when the corresponding argument was False
  199. Raises:
  200. MissingCredentialsError
  201. InvalidCredentialsError
  202. ExpiredCredentialsError
  203. """
  204. res = rcreds.inquire_cred(self, name, lifetime, usage, mechs)
  205. if res.name is not None:
  206. res_name = names.Name(res.name)
  207. else:
  208. res_name = None
  209. return tuples.InquireCredResult(res_name, res.lifetime,
  210. res.usage, res.mechs)
  211. def inquire_by_mech(self, mech, name=True, init_lifetime=True,
  212. accept_lifetime=True, usage=True):
  213. """Inspect these credentials for per-mechanism information
  214. This method inspects these credentials for per-mechanism information
  215. about them.
  216. Args:
  217. mech (OID): the mechanism for which to retrive the information
  218. name (bool): get the name associated with the credentials
  219. init_lifetime (bool): get the remaining initiate lifetime for
  220. the credentials
  221. accept_lifetime (bool): get the remaining accept lifetime for
  222. the credentials
  223. usage (bool): get the usage for the credentials
  224. Returns:
  225. InquireCredByMechResult: the information about the credentials,
  226. with None used when the corresponding argument was False
  227. """
  228. res = rcreds.inquire_cred_by_mech(self, mech, name, init_lifetime,
  229. accept_lifetime, usage)
  230. if res.name is not None:
  231. res_name = names.Name(res.name)
  232. else:
  233. res_name = None
  234. return tuples.InquireCredByMechResult(res_name,
  235. res.init_lifetime,
  236. res.accept_lifetime,
  237. res.usage)
  238. def add(self, name, mech, usage='both',
  239. init_lifetime=None, accept_lifetime=None, impersonator=None,
  240. store=None):
  241. """Acquire more credentials to add to the current set
  242. This method works like :meth:`acquire`, except that it adds the
  243. acquired credentials for a single mechanism to a copy of the current
  244. set, instead of creating a new set for multiple mechanisms.
  245. Unlike :meth:`acquire`, you cannot pass None desired name or
  246. mechanism.
  247. If the `impersonator` argument is used, the credentials will
  248. impersonate the given name using the impersonator credentials
  249. (:requires-ext:`s4u`).
  250. If the `store` argument is used, the credentials will be acquired
  251. from the given credential store (:requires-ext:`cred_store`).
  252. Otherwise, the credentials are acquired from the default store.
  253. The credential store information is a dictionary containing
  254. mechanisms-specific keys and values pointing to a credential store
  255. or stores.
  256. Note that the `store` argument is not compatible with the
  257. `impersonator` argument.
  258. Args:
  259. name (Name): the name associated with the
  260. credentials
  261. mech (OID): the desired :class:`MechType` to be used with the
  262. credentials
  263. usage (str): the usage for the credentials -- either 'both',
  264. 'initiate', or 'accept'
  265. init_lifetime (int): the desired initiate lifetime of the
  266. credentials, or None for indefinite
  267. accept_lifetime (int): the desired accept lifetime of the
  268. credentials, or None for indefinite
  269. impersonator (Credentials): the credentials to use to impersonate
  270. the given name, or None to not acquire normally
  271. (:requires-ext:`s4u`)
  272. store (dict): the credential store information pointing to the
  273. credential store from which to acquire the credentials,
  274. or None for the default store (:requires-ext:`cred_store`)
  275. Returns:
  276. Credentials: the credentials set containing the current credentials
  277. and the newly acquired ones.
  278. Raises:
  279. BadMechanismError
  280. BadNameTypeError
  281. BadNameError
  282. DuplicateCredentialsElementError
  283. ExpiredCredentialsError
  284. MissingCredentialsError
  285. """
  286. if store is not None and impersonator is not None:
  287. raise ValueError('You cannot use both the `impersonator` and '
  288. '`store` arguments at the same time')
  289. if store is not None:
  290. if rcred_cred_store is None:
  291. raise NotImplementedError("Your GSSAPI implementation does "
  292. "not have support for manipulating "
  293. "credential stores")
  294. store = _encode_dict(store)
  295. res = rcred_cred_store.add_cred_from(store, self, name, mech,
  296. usage, init_lifetime,
  297. accept_lifetime)
  298. elif impersonator is not None:
  299. if rcred_s4u is None:
  300. raise NotImplementedError("Your GSSAPI implementation does "
  301. "not have support for S4U")
  302. res = rcred_s4u.add_cred_impersonate_name(self, impersonator,
  303. name, mech, usage,
  304. init_lifetime,
  305. accept_lifetime)
  306. else:
  307. res = rcreds.add_cred(self, name, mech, usage, init_lifetime,
  308. accept_lifetime)
  309. return Credentials(res.creds)
  310. def export(self):
  311. """Export these credentials into a token
  312. This method exports the current credentials to a token that can
  313. then be imported by passing the `token` argument to the constructor.
  314. This is often used to pass credentials between processes.
  315. :requires-ext:`cred_imp_exp`
  316. Returns:
  317. bytes: the exported credentials in token form
  318. """
  319. if rcred_imp_exp is None:
  320. raise NotImplementedError("Your GSSAPI implementation does not "
  321. "have support for importing and "
  322. "exporting creditials")
  323. return rcred_imp_exp.export_cred(self)
  324. # pickle protocol support
  325. def __reduce__(self):
  326. # the unpickle arguments to new are (base=None, token=self.export())
  327. return (type(self), (None, self.export()))