mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Added vault-archive and vault-retrieve commands.
New commands have been added to archive and retrieve data into and from a vault, also to retrieve the transport certificate. https://fedorahosted.org/freeipa/ticket/3872 Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
parent
e01095dfb3
commit
df1bd39a43
65
API.txt
65
API.txt
@ -5146,6 +5146,36 @@ option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_archive
|
||||
args: 1,8,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Bytes('data?')
|
||||
option: Str('in?')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('service?')
|
||||
option: Flag('shared?', autofill=True, default=False)
|
||||
option: Str('user?')
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_archive_encrypted
|
||||
args: 1,10,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
|
||||
option: Bytes('nonce')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('service?')
|
||||
option: Bytes('session_key')
|
||||
option: Flag('shared?', autofill=True, default=False)
|
||||
option: Str('user?')
|
||||
option: Bytes('vault_data')
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_del
|
||||
args: 1,5,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
@ -5192,6 +5222,32 @@ option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_retrieve
|
||||
args: 1,7,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Str('out?')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('service?')
|
||||
option: Flag('shared?', autofill=True, default=False)
|
||||
option: Str('user?')
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_retrieve_encrypted
|
||||
args: 1,7,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('service?')
|
||||
option: Bytes('session_key')
|
||||
option: Flag('shared?', autofill=True, default=False)
|
||||
option: Str('user?')
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vault_show
|
||||
args: 1,7,3
|
||||
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
|
||||
@ -5205,6 +5261,15 @@ option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
command: vaultconfig_show
|
||||
args: 0,4,3
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('transport_out?')
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
output: PrimaryKey('value', None, None)
|
||||
capability: messages 2.52
|
||||
capability: optional_uid_params 2.54
|
||||
capability: permissions2 2.69
|
||||
|
4
VERSION
4
VERSION
@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
|
||||
# #
|
||||
########################################################
|
||||
IPA_API_VERSION_MAJOR=2
|
||||
IPA_API_VERSION_MINOR=125
|
||||
# Last change: derny - migration now accepts scope as argument
|
||||
IPA_API_VERSION_MINOR=126
|
||||
# Last change: edewata - added vault-archive and vault-retrieve
|
||||
|
@ -17,16 +17,33 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import nss.nss as nss
|
||||
import krbV
|
||||
|
||||
from ipalib.frontend import Command, Object, Local
|
||||
from ipalib import api, errors
|
||||
from ipalib import Str, Flag
|
||||
from ipalib import Bytes, Str, Flag
|
||||
from ipalib import output
|
||||
from ipalib.crud import PKQuery, Retrieve, Update
|
||||
from ipalib.plugable import Registry
|
||||
from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete,\
|
||||
LDAPSearch, LDAPUpdate, LDAPRetrieve
|
||||
from ipalib.request import context
|
||||
from ipalib.plugins.user import split_principal
|
||||
from ipalib import _, ngettext
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipapython.nsslib import current_dbdir
|
||||
|
||||
if api.env.in_server:
|
||||
import pki.account
|
||||
import pki.key
|
||||
|
||||
__doc__ = _("""
|
||||
Vaults
|
||||
@ -94,6 +111,33 @@ EXAMPLES:
|
||||
""") + _("""
|
||||
Delete a user vault:
|
||||
ipa vault-del <name> --user <username>
|
||||
""") + _("""
|
||||
Display vault configuration:
|
||||
ipa vault-config
|
||||
""") + _("""
|
||||
Archive data into private vault:
|
||||
ipa vault-archive <name> --in <input file>
|
||||
""") + _("""
|
||||
Archive data into service vault:
|
||||
ipa vault-archive <name> --service <service name> --in <input file>
|
||||
""") + _("""
|
||||
Archive data into shared vault:
|
||||
ipa vault-archive <name> --shared --in <input file>
|
||||
""") + _("""
|
||||
Archive data into user vault:
|
||||
ipa vault-archive <name> --user <username> --in <input file>
|
||||
""") + _("""
|
||||
Retrieve data from private vault:
|
||||
ipa vault-retrieve <name> --out <output file>
|
||||
""") + _("""
|
||||
Retrieve data from service vault:
|
||||
ipa vault-retrieve <name> --service <service name> --out <output file>
|
||||
""") + _("""
|
||||
Retrieve data from shared vault:
|
||||
ipa vault-retrieve <name> --shared --out <output file>
|
||||
""") + _("""
|
||||
Retrieve data from user vault:
|
||||
ipa vault-retrieve <name> --user <user name> --out <output file>
|
||||
""")
|
||||
|
||||
register = Registry()
|
||||
@ -243,6 +287,26 @@ class vault(LDAPObject):
|
||||
for entry in entries:
|
||||
self.backend.add_entry(entry)
|
||||
|
||||
def get_key_id(self, dn):
|
||||
"""
|
||||
Generates a client key ID to archive/retrieve data in KRA.
|
||||
"""
|
||||
|
||||
# TODO: create container_dn after object initialization then reuse it
|
||||
container_dn = DN(self.container_dn, self.api.env.basedn)
|
||||
|
||||
# make sure the DN is a vault DN
|
||||
if not dn.endswith(container_dn, 1):
|
||||
raise ValueError('Invalid vault DN: %s' % dn)
|
||||
|
||||
# construct the vault ID from the bottom up
|
||||
id = u''
|
||||
for rdn in dn[:-len(container_dn)]:
|
||||
name = rdn['cn']
|
||||
id = u'/' + name + id
|
||||
|
||||
return 'ipa:' + id
|
||||
|
||||
|
||||
@register()
|
||||
class vault_add(LDAPCreate):
|
||||
@ -256,6 +320,10 @@ class vault_add(LDAPCreate):
|
||||
**options):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
try:
|
||||
parent_dn = DN(*dn[1:])
|
||||
self.obj.create_container(parent_dn)
|
||||
@ -273,6 +341,38 @@ class vault_del(LDAPDelete):
|
||||
|
||||
msg_summary = _('Deleted vault "%(value)s"')
|
||||
|
||||
def pre_callback(self, ldap, dn, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
return dn
|
||||
|
||||
def post_callback(self, ldap, dn, *args, **options):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
kra_client = self.api.Backend.kra.get_client()
|
||||
|
||||
kra_account = pki.account.AccountClient(kra_client.connection)
|
||||
kra_account.login()
|
||||
|
||||
client_key_id = self.obj.get_key_id(dn)
|
||||
|
||||
# deactivate vault record in KRA
|
||||
response = kra_client.keys.list_keys(
|
||||
client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE)
|
||||
|
||||
for key_info in response.key_infos:
|
||||
kra_client.keys.modify_key_status(
|
||||
key_info.get_key_id(),
|
||||
pki.key.KeyClient.KEY_STATUS_INACTIVE)
|
||||
|
||||
kra_account.logout()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@register()
|
||||
class vault_find(LDAPSearch):
|
||||
@ -290,6 +390,10 @@ class vault_find(LDAPSearch):
|
||||
**options):
|
||||
assert isinstance(base_dn, DN)
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
base_dn = self.obj.get_dn(*args, **options)
|
||||
|
||||
return (filter, base_dn, scope)
|
||||
@ -313,9 +417,399 @@ class vault_mod(LDAPUpdate):
|
||||
|
||||
msg_summary = _('Modified vault "%(value)s"')
|
||||
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list,
|
||||
*keys, **options):
|
||||
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
return dn
|
||||
|
||||
|
||||
@register()
|
||||
class vault_show(LDAPRetrieve):
|
||||
__doc__ = _('Display information about a vault.')
|
||||
|
||||
takes_options = LDAPRetrieve.takes_options + vault_options
|
||||
|
||||
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
return dn
|
||||
|
||||
|
||||
@register()
|
||||
class vaultconfig(Object):
|
||||
__doc__ = _('Vault configuration')
|
||||
|
||||
takes_params = (
|
||||
Bytes(
|
||||
'transport_cert',
|
||||
label=_('Transport Certificate'),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@register()
|
||||
class vaultconfig_show(Retrieve):
|
||||
__doc__ = _('Show vault configuration.')
|
||||
|
||||
takes_options = (
|
||||
Str(
|
||||
'transport_out?',
|
||||
doc=_('Output file to store the transport certificate'),
|
||||
),
|
||||
)
|
||||
|
||||
def forward(self, *args, **options):
|
||||
|
||||
file = options.get('transport_out')
|
||||
|
||||
# don't send these parameters to server
|
||||
if 'transport_out' in options:
|
||||
del options['transport_out']
|
||||
|
||||
response = super(vaultconfig_show, self).forward(*args, **options)
|
||||
|
||||
if file:
|
||||
with open(file, 'w') as f:
|
||||
f.write(response['result']['transport_cert'])
|
||||
|
||||
return response
|
||||
|
||||
def execute(self, *args, **options):
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
kra_client = self.api.Backend.kra.get_client()
|
||||
transport_cert = kra_client.system_certs.get_transport_cert()
|
||||
return {
|
||||
'result': {
|
||||
'transport_cert': transport_cert.binary
|
||||
},
|
||||
'value': None,
|
||||
}
|
||||
|
||||
|
||||
@register()
|
||||
class vault_archive(PKQuery, Local):
|
||||
__doc__ = _('Archive data into a vault.')
|
||||
|
||||
takes_options = vault_options + (
|
||||
Bytes(
|
||||
'data?',
|
||||
doc=_('Binary data to archive'),
|
||||
),
|
||||
Str( # TODO: use File parameter
|
||||
'in?',
|
||||
doc=_('File containing data to archive'),
|
||||
),
|
||||
)
|
||||
|
||||
has_output = output.standard_entry
|
||||
|
||||
msg_summary = _('Archived data into vault "%(value)s"')
|
||||
|
||||
def forward(self, *args, **options):
|
||||
|
||||
data = options.get('data')
|
||||
input_file = options.get('in')
|
||||
|
||||
# don't send these parameters to server
|
||||
if 'data' in options:
|
||||
del options['data']
|
||||
if 'in' in options:
|
||||
del options['in']
|
||||
|
||||
# get data
|
||||
if data and input_file:
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_('Input data specified multiple times'))
|
||||
|
||||
if input_file:
|
||||
with open(input_file, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
elif not data:
|
||||
data = ''
|
||||
|
||||
if self.api.env.in_server:
|
||||
backend = self.api.Backend.ldap2
|
||||
else:
|
||||
backend = self.api.Backend.rpcclient
|
||||
if not backend.isconnected():
|
||||
backend.connect(ccache=krbV.default_context().default_ccache())
|
||||
|
||||
# initialize NSS database
|
||||
current_dbdir = paths.IPA_NSSDB_DIR
|
||||
nss.nss_init(current_dbdir)
|
||||
|
||||
# retrieve transport certificate
|
||||
config = self.api.Command.vaultconfig_show()
|
||||
transport_cert_der = config['result']['transport_cert']
|
||||
nss_transport_cert = nss.Certificate(transport_cert_der)
|
||||
|
||||
# generate session key
|
||||
mechanism = nss.CKM_DES3_CBC_PAD
|
||||
slot = nss.get_best_slot(mechanism)
|
||||
key_length = slot.get_best_key_length(mechanism)
|
||||
session_key = slot.key_gen(mechanism, None, key_length)
|
||||
|
||||
# wrap session key with transport certificate
|
||||
public_key = nss_transport_cert.subject_public_key_info.public_key
|
||||
wrapped_session_key = nss.pub_wrap_sym_key(mechanism,
|
||||
public_key,
|
||||
session_key)
|
||||
|
||||
options['session_key'] = wrapped_session_key.data
|
||||
|
||||
nonce_length = nss.get_iv_length(mechanism)
|
||||
nonce = nss.generate_random(nonce_length)
|
||||
options['nonce'] = nonce
|
||||
|
||||
vault_data = {}
|
||||
vault_data[u'data'] = base64.b64encode(data).decode('utf-8')
|
||||
|
||||
json_vault_data = json.dumps(vault_data)
|
||||
|
||||
# wrap vault_data with session key
|
||||
iv_si = nss.SecItem(nonce)
|
||||
iv_param = nss.param_from_iv(mechanism, iv_si)
|
||||
|
||||
encoding_ctx = nss.create_context_by_sym_key(mechanism,
|
||||
nss.CKA_ENCRYPT,
|
||||
session_key,
|
||||
iv_param)
|
||||
|
||||
wrapped_vault_data = encoding_ctx.cipher_op(json_vault_data)\
|
||||
+ encoding_ctx.digest_final()
|
||||
|
||||
options['vault_data'] = wrapped_vault_data
|
||||
|
||||
response = self.api.Command.vault_archive_encrypted(*args, **options)
|
||||
|
||||
response['result'] = {}
|
||||
del response['summary']
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@register()
|
||||
class vault_archive_encrypted(Update):
|
||||
NO_CLI = True
|
||||
|
||||
takes_options = vault_options + (
|
||||
Bytes(
|
||||
'session_key',
|
||||
doc=_('Session key wrapped with transport certificate'),
|
||||
),
|
||||
Bytes(
|
||||
'vault_data',
|
||||
doc=_('Vault data encrypted with session key'),
|
||||
),
|
||||
Bytes(
|
||||
'nonce',
|
||||
doc=_('Nonce'),
|
||||
),
|
||||
)
|
||||
|
||||
def execute(self, *args, **options):
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
wrapped_vault_data = options.pop('vault_data')
|
||||
nonce = options.pop('nonce')
|
||||
wrapped_session_key = options.pop('session_key')
|
||||
|
||||
# retrieve vault info
|
||||
result = self.api.Command.vault_show(*args, **options)
|
||||
vault = result['result']
|
||||
|
||||
# connect to KRA
|
||||
kra_client = self.api.Backend.kra.get_client()
|
||||
|
||||
kra_account = pki.account.AccountClient(kra_client.connection)
|
||||
kra_account.login()
|
||||
|
||||
client_key_id = self.obj.get_key_id(vault['dn'])
|
||||
|
||||
# deactivate existing vault record in KRA
|
||||
response = kra_client.keys.list_keys(
|
||||
client_key_id,
|
||||
pki.key.KeyClient.KEY_STATUS_ACTIVE)
|
||||
|
||||
for key_info in response.key_infos:
|
||||
kra_client.keys.modify_key_status(
|
||||
key_info.get_key_id(),
|
||||
pki.key.KeyClient.KEY_STATUS_INACTIVE)
|
||||
|
||||
# forward wrapped data to KRA
|
||||
kra_client.keys.archive_encrypted_data(
|
||||
client_key_id,
|
||||
pki.key.KeyClient.PASS_PHRASE_TYPE,
|
||||
wrapped_vault_data,
|
||||
wrapped_session_key,
|
||||
None,
|
||||
nonce,
|
||||
)
|
||||
|
||||
kra_account.logout()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@register()
|
||||
class vault_retrieve(PKQuery, Local):
|
||||
__doc__ = _('Retrieve a data from a vault.')
|
||||
|
||||
takes_options = vault_options + (
|
||||
Str(
|
||||
'out?',
|
||||
doc=_('File to store retrieved data'),
|
||||
),
|
||||
)
|
||||
|
||||
has_output = output.standard_entry
|
||||
has_output_params = (
|
||||
Bytes(
|
||||
'data',
|
||||
label=_('Data'),
|
||||
),
|
||||
)
|
||||
|
||||
msg_summary = _('Retrieved data from vault "%(value)s"')
|
||||
|
||||
def forward(self, *args, **options):
|
||||
|
||||
output_file = options.get('out')
|
||||
|
||||
# don't send these parameters to server
|
||||
if 'out' in options:
|
||||
del options['out']
|
||||
|
||||
if self.api.env.in_server:
|
||||
backend = self.api.Backend.ldap2
|
||||
else:
|
||||
backend = self.api.Backend.rpcclient
|
||||
if not backend.isconnected():
|
||||
backend.connect(ccache=krbV.default_context().default_ccache())
|
||||
|
||||
# initialize NSS database
|
||||
current_dbdir = paths.IPA_NSSDB_DIR
|
||||
nss.nss_init(current_dbdir)
|
||||
|
||||
# retrieve transport certificate
|
||||
config = self.api.Command.vaultconfig_show()
|
||||
transport_cert_der = config['result']['transport_cert']
|
||||
nss_transport_cert = nss.Certificate(transport_cert_der)
|
||||
|
||||
# generate session key
|
||||
mechanism = nss.CKM_DES3_CBC_PAD
|
||||
slot = nss.get_best_slot(mechanism)
|
||||
key_length = slot.get_best_key_length(mechanism)
|
||||
session_key = slot.key_gen(mechanism, None, key_length)
|
||||
|
||||
# wrap session key with transport certificate
|
||||
public_key = nss_transport_cert.subject_public_key_info.public_key
|
||||
wrapped_session_key = nss.pub_wrap_sym_key(mechanism,
|
||||
public_key,
|
||||
session_key)
|
||||
|
||||
# send retrieval request to server
|
||||
options['session_key'] = wrapped_session_key.data
|
||||
|
||||
response = self.api.Command.vault_retrieve_encrypted(*args, **options)
|
||||
|
||||
result = response['result']
|
||||
nonce = result['nonce']
|
||||
|
||||
# unwrap data with session key
|
||||
wrapped_vault_data = result['vault_data']
|
||||
|
||||
iv_si = nss.SecItem(nonce)
|
||||
iv_param = nss.param_from_iv(mechanism, iv_si)
|
||||
|
||||
decoding_ctx = nss.create_context_by_sym_key(mechanism,
|
||||
nss.CKA_DECRYPT,
|
||||
session_key,
|
||||
iv_param)
|
||||
|
||||
json_vault_data = decoding_ctx.cipher_op(wrapped_vault_data)\
|
||||
+ decoding_ctx.digest_final()
|
||||
|
||||
vault_data = json.loads(json_vault_data)
|
||||
data = base64.b64decode(vault_data[u'data'].encode('utf-8'))
|
||||
|
||||
if output_file:
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
response['result'] = {'data': data}
|
||||
del response['summary']
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@register()
|
||||
class vault_retrieve_encrypted(Retrieve):
|
||||
NO_CLI = True
|
||||
|
||||
takes_options = vault_options + (
|
||||
Bytes(
|
||||
'session_key',
|
||||
doc=_('Session key wrapped with transport certificate'),
|
||||
),
|
||||
)
|
||||
|
||||
def execute(self, *args, **options):
|
||||
|
||||
if not self.api.env.enable_kra:
|
||||
raise errors.InvocationError(
|
||||
format=_('KRA service is not enabled'))
|
||||
|
||||
wrapped_session_key = options.pop('session_key')
|
||||
|
||||
# retrieve vault info
|
||||
result = self.api.Command.vault_show(*args, **options)
|
||||
vault = result['result']
|
||||
|
||||
# connect to KRA
|
||||
kra_client = self.api.Backend.kra.get_client()
|
||||
|
||||
kra_account = pki.account.AccountClient(kra_client.connection)
|
||||
kra_account.login()
|
||||
|
||||
client_key_id = self.obj.get_key_id(vault['dn'])
|
||||
|
||||
# find vault record in KRA
|
||||
response = kra_client.keys.list_keys(
|
||||
client_key_id,
|
||||
pki.key.KeyClient.KEY_STATUS_ACTIVE)
|
||||
|
||||
if not len(response.key_infos):
|
||||
raise errors.NotFound(reason=_('No archived data.'))
|
||||
|
||||
key_info = response.key_infos[0]
|
||||
|
||||
# retrieve encrypted data from KRA
|
||||
key = kra_client.keys.retrieve_key(
|
||||
key_info.get_key_id(),
|
||||
wrapped_session_key)
|
||||
|
||||
vault['vault_data'] = key.encrypted_data
|
||||
vault['nonce'] = key.nonce_data
|
||||
|
||||
kra_account.logout()
|
||||
|
||||
return result
|
||||
|
@ -22,12 +22,15 @@ Test the `ipalib/plugins/vault.py` module.
|
||||
"""
|
||||
|
||||
from ipalib import api, errors
|
||||
from xmlrpc_test import Declarative, fuzzy_string
|
||||
from xmlrpc_test import Declarative
|
||||
|
||||
vault_name = u'test_vault'
|
||||
service_name = u'HTTP/server.example.com'
|
||||
user_name = u'testuser'
|
||||
|
||||
# binary data from \x00 to \xff
|
||||
secret = ''.join(map(chr, xrange(0, 256)))
|
||||
|
||||
|
||||
class test_vault_plugin(Declarative):
|
||||
|
||||
@ -442,4 +445,71 @@ class test_vault_plugin(Declarative):
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
'desc': 'Create vault for archival',
|
||||
'command': (
|
||||
'vault_add',
|
||||
[vault_name],
|
||||
{},
|
||||
),
|
||||
'expected': {
|
||||
'value': vault_name,
|
||||
'summary': 'Added vault "%s"' % vault_name,
|
||||
'result': {
|
||||
'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s'
|
||||
% (vault_name, api.env.basedn),
|
||||
'objectclass': [u'top', u'ipaVault'],
|
||||
'cn': [vault_name],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
'desc': 'Archive secret',
|
||||
'command': (
|
||||
'vault_archive',
|
||||
[vault_name],
|
||||
{
|
||||
'data': secret,
|
||||
},
|
||||
),
|
||||
'expected': {
|
||||
'value': vault_name,
|
||||
'summary': 'Archived data into vault "%s"' % vault_name,
|
||||
'result': {},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
'desc': 'Retrieve secret',
|
||||
'command': (
|
||||
'vault_retrieve',
|
||||
[vault_name],
|
||||
{},
|
||||
),
|
||||
'expected': {
|
||||
'value': vault_name,
|
||||
'summary': 'Retrieved data from vault "%s"' % vault_name,
|
||||
'result': {
|
||||
'data': secret,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
'desc': 'Delete vault for archival',
|
||||
'command': (
|
||||
'vault_del',
|
||||
[vault_name],
|
||||
{},
|
||||
),
|
||||
'expected': {
|
||||
'value': [vault_name],
|
||||
'summary': u'Deleted vault "%s"' % vault_name,
|
||||
'result': {
|
||||
'failed': (),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
]
|
||||
|
@ -62,6 +62,7 @@ class IPATypeChecker(TypeChecker):
|
||||
'unittest.case': ['assertEqual', 'assertRaises'],
|
||||
'nose.tools': ['assert_equal', 'assert_raises'],
|
||||
'datetime.tzinfo': ['houroffset', 'minoffset', 'utcoffset', 'dst'],
|
||||
'nss.nss.subject_public_key_info': ['public_key'],
|
||||
|
||||
# IPA classes
|
||||
'ipalib.base.NameSpace': ['add', 'mod', 'del', 'show', 'find'],
|
||||
|
Loading…
Reference in New Issue
Block a user