From d4969ede51e6098e962ff660daf13e8c61d4ac28 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 31 Jul 2015 07:53:15 +0200 Subject: Added support for changing vault encryption. The vault-mod command has been modified to support changing vault encryption attributes (i.e. type, password, public/private keys) in addition to normal attributes (i.e. description). Changing the encryption requires retrieving the stored secret with the old attributes and rearchiving it with the new attributes. https://fedorahosted.org/freeipa/ticket/5176 Reviewed-By: Martin Basti --- ipalib/plugins/vault.py | 233 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 221 insertions(+), 12 deletions(-) (limited to 'ipalib') diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 4b2c8a518..6a07a76b5 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -116,10 +116,36 @@ EXAMPLES: ipa vault-show [--user |--service |--shared] """) + _(""" - Modify a vault: + Modify vault description: ipa vault-mod [--user |--service |--shared] --desc +""") + _(""" + Modify vault type: + ipa vault-mod + [--user |--service |--shared] + --type + [old password/private key] + [new password/public key] +""") + _(""" + Modify symmetric vault password: + ipa vault-mod + [--user |--service |--shared] + --change-password + ipa vault-mod + [--user |--service |--shared] + --old-password + --new-password + ipa vault-mod + [--user |--service |--shared] + --old-password-file + --new-password-file +""") + _(""" + Modify asymmetric vault keys: + ipa vault-mod + [--user |--service |--shared] + --private-key-file + --public-key-file """) + _(""" Delete a vault: ipa vault-del @@ -457,7 +483,7 @@ class vault(LDAPObject): print ' ** Passwords do not match! **' - def get_existing_password(self, new=False): + def get_existing_password(self): """ Gets existing password from user. """ @@ -871,9 +897,182 @@ class vault_find(LDAPSearch): @register() -class vault_mod(LDAPUpdate): +class vault_mod(PKQuery, Local): __doc__ = _('Modify a vault.') + takes_options = vault_options + ( + Str( + 'description?', + cli_name='desc', + doc=_('Vault description'), + ), + Str( + 'ipavaulttype?', + cli_name='type', + doc=_('Vault type'), + ), + Bytes( + 'ipavaultsalt?', + cli_name='salt', + doc=_('Vault salt'), + ), + Flag( + 'change_password?', + doc=_('Change password'), + ), + Str( + 'old_password?', + cli_name='old_password', + doc=_('Old vault password'), + ), + Str( # TODO: use File parameter + 'old_password_file?', + cli_name='old_password_file', + doc=_('File containing the old vault password'), + ), + Str( + 'new_password?', + cli_name='new_password', + doc=_('New vault password'), + ), + Str( # TODO: use File parameter + 'new_password_file?', + cli_name='new_password_file', + doc=_('File containing the new vault password'), + ), + Bytes( + 'private_key?', + cli_name='private_key', + doc=_('Old vault private key'), + ), + Str( # TODO: use File parameter + 'private_key_file?', + cli_name='private_key_file', + doc=_('File containing the old vault private key'), + ), + Bytes( + 'ipavaultpublickey?', + cli_name='public_key', + doc=_('New vault public key'), + ), + Str( # TODO: use File parameter + 'public_key_file?', + cli_name='public_key_file', + doc=_('File containing the new vault public key'), + ), + ) + + has_output = output.standard_entry + + def forward(self, *args, **options): + + vault_type = options.pop('ipavaulttype', False) + salt = options.pop('ipavaultsalt', False) + change_password = options.pop('change_password', False) + + old_password = options.pop('old_password', None) + old_password_file = options.pop('old_password_file', None) + new_password = options.pop('new_password', None) + new_password_file = options.pop('new_password_file', None) + + old_private_key = options.pop('private_key', None) + old_private_key_file = options.pop('private_key_file', None) + new_public_key = options.pop('ipavaultpublickey', None) + new_public_key_file = options.pop('public_key_file', None) + + 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()) + + # determine the vault type based on parameters specified + if vault_type: + pass + + elif change_password or new_password or new_password_file or salt: + vault_type = u'symmetric' + + elif new_public_key or new_public_key_file: + vault_type = u'asymmetric' + + # if vault type is specified, retrieve existing secret + if vault_type: + opts = options.copy() + opts.pop('description', None) + + opts['password'] = old_password + opts['password_file'] = old_password_file + opts['private_key'] = old_private_key + opts['private_key_file'] = old_private_key_file + + response = self.api.Command.vault_retrieve(*args, **opts) + data = response['result']['data'] + + opts = options.copy() + + # if vault type is specified, update crypto attributes + if vault_type: + opts['ipavaulttype'] = vault_type + + if vault_type == u'standard': + opts['ipavaultsalt'] = None + opts['ipavaultpublickey'] = None + + elif vault_type == u'symmetric': + if salt: + opts['ipavaultsalt'] = salt + else: + opts['ipavaultsalt'] = os.urandom(16) + + opts['ipavaultpublickey'] = None + + elif vault_type == u'asymmetric': + + # get new vault public key + if new_public_key and new_public_key_file: + raise errors.MutuallyExclusiveError( + reason=_('New public key specified multiple times')) + + elif new_public_key: + pass + + elif new_public_key_file: + new_public_key = validated_read('public_key_file', + new_public_key_file, + mode='rb') + + else: + raise errors.ValidationError( + name='ipavaultpublickey', + error=_('Missing new vault public key')) + + opts['ipavaultsalt'] = None + opts['ipavaultpublickey'] = new_public_key + + response = self.api.Command.vault_mod_internal(*args, **opts) + + # if vault type is specified, rearchive existing secret + if vault_type: + opts = options.copy() + opts.pop('description', None) + + opts['data'] = data + opts['password'] = new_password + opts['password_file'] = new_password_file + opts['override_password'] = True + + self.api.Command.vault_archive(*args, **opts) + + return response + + +@register() +class vault_mod_internal(LDAPUpdate): + + NO_CLI = True + takes_options = LDAPUpdate.takes_options + vault_options msg_summary = _('Modified vault "%(value)s"') @@ -994,6 +1193,10 @@ class vault_archive(PKQuery, Local): cli_name='password_file', doc=_('File containing the vault password'), ), + Flag( + 'override_password?', + doc=_('Override existing password'), + ), ) has_output = output.standard_entry @@ -1008,6 +1211,8 @@ class vault_archive(PKQuery, Local): password = options.get('password') password_file = options.get('password_file') + override_password = options.pop('override_password', False) + # don't send these parameters to server if 'data' in options: del options['data'] @@ -1062,15 +1267,19 @@ class vault_archive(PKQuery, Local): password = password.rstrip('\n') else: - password = self.obj.get_existing_password() - - # verify password by retrieving existing data - opts = options.copy() - opts['password'] = password - try: - self.api.Command.vault_retrieve(*args, **opts) - except errors.NotFound: - pass + if override_password: + password = self.obj.get_new_password() + else: + password = self.obj.get_existing_password() + + if not override_password: + # verify password by retrieving existing data + opts = options.copy() + opts['password'] = password + try: + self.api.Command.vault_retrieve(*args, **opts) + except errors.NotFound: + pass salt = vault['ipavaultsalt'][0] -- cgit