On 10/22/2014 3:04 PM, Endi Sukma Dewata wrote:
On 10/16/2014 4:12 PM, Endi Sukma Dewata wrote:
On 10/15/2014 10:59 PM, Endi Sukma Dewata wrote:
The NSSConnection class has to be modified not to shutdown existing
database because some of the vault clients (e.g. vault-archive and
vault-retrieve) also use a database to encrypt/decrypt the secret.
The problem is described in more detail in this ticket:
https://fedorahosted.org/freeipa/ticket/4638
The changes to the NSSConnection in the first patch caused the
installation to fail. Attached is a new patch that uses the solution
proposed by jdennis.
New patch attached. It's now using the correct OID's for the schema. It
also has been rebased on top of #352-1 and #354.
New patch attached to fix the ticket URL. It depends on #352-2 and #354-1.
--
Endi S. Dewata
>From cd3daa901f7139801ea61ae1e2716810da131bcc Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edew...@redhat.com>
Date: Tue, 21 Oct 2014 10:57:08 -0400
Subject: [PATCH] Added initial vault implementation.
This patch provides the initial vault implementation which allows
the admin to create a vault, archive a secret, and retrieve the
secret using a standard vault. It also included the initial LDAP
schema.
It currently has limitations including:
- The vault only supports the standard vault type.
- The vault can only be used by the admin user.
- The transport certificate has to be installed manually.
These limitations, other vault features, schema and ACL changes will
be addressed in subsequent patches.
https://fedorahosted.org/freeipa/ticket/3872
---
API.txt | 160 ++++++++
VERSION | 4 +-
install/share/60basev4.ldif | 3 +
install/share/Makefile.am | 1 +
install/share/copy-schema-to-ca.py | 1 +
install/updates/40-vault.update | 27 ++
install/updates/Makefile.am | 1 +
ipa-client/man/default.conf.5 | 1 +
ipalib/constants.py | 1 +
ipalib/plugins/user.py | 9 +
ipalib/plugins/vault.py | 724 +++++++++++++++++++++++++++++++++++++
ipaserver/install/dsinstance.py | 1 +
12 files changed, 931 insertions(+), 2 deletions(-)
create mode 100644 install/share/60basev4.ldif
create mode 100644 install/updates/40-vault.update
create mode 100644 ipalib/plugins/vault.py
diff --git a/API.txt b/API.txt
index
0000491d7a76fd1d2d50208d314d1600839ce295..cfa6558fcf678e5915a90407da517f9a591a41bf
100644
--- a/API.txt
+++ b/API.txt
@@ -4475,6 +4475,166 @@ option: Str('version?', exclude='webui')
output: Output('result', <type 'bool'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
+command: vault_add
+args: 1,8,3
+arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('description', attribute=True, cli_name='desc', multivalue=False,
required=False)
+option: Str('in?', cli_name='in')
+option: Str('parent', attribute=False, cli_name='parent', multivalue=False,
required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Bytes('secret', attribute=True, cli_name='secret', multivalue=False,
required=False)
+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,10,3
+arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[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('encrypted_data?', cli_name='encrypted_data')
+option: Str('in?', cli_name='in')
+option: Bytes('nonce?', cli_name='nonce')
+option: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Bytes('secret?', cli_name='secret')
+option: Str('version?', exclude='webui')
+option: Bytes('wrapped_session_key?', cli_name='wrapped_session_key')
+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,3,3
+arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255,
multivalue=True,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Str('parent?', cli_name='parent')
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'dict'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfPrimaryKeys('value', None, None)
+command: vault_find
+args: 1,10,4
+arg: Str('criteria?', noextrawhitespace=False)
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('cn', attribute=True, autofill=False, cli_name='vault_name',
maxlength=255, multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=False)
+option: Str('description', attribute=True, autofill=False, cli_name='desc',
multivalue=False, query=True, required=False)
+option: Str('parent', attribute=False, autofill=False, cli_name='parent',
multivalue=False, query=True, required=False)
+option: Flag('pkey_only?', autofill=True, default=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Bytes('secret', attribute=True, autofill=False, cli_name='secret',
multivalue=False, query=True, required=False)
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Str('version?', exclude='webui')
+output: Output('count', <type 'int'>, None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A
list of LDAP entries', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('truncated', <type 'bool'>, None)
+command: vault_mod
+args: 1,10,3
+arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Str('description', attribute=True, autofill=False, cli_name='desc',
multivalue=False, required=False)
+option: Str('parent', attribute=False, autofill=False, cli_name='parent',
multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Bytes('secret', attribute=True, autofill=False, cli_name='secret',
multivalue=False, required=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+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='vault_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[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?', cli_name='out')
+option: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('version?', exclude='webui')
+option: Bytes('wrapped_session_key?', cli_name='wrapped_session_key')
+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,5,3
+arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[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('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+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: vaultcontainer_add
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('description', attribute=True, cli_name='desc', multivalue=False,
required=False)
+option: Str('parent', attribute=False, cli_name='parent', multivalue=False,
required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+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: vaultcontainer_del
+args: 1,3,3
+arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255,
multivalue=True,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Str('parent?', cli_name='parent')
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'dict'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfPrimaryKeys('value', None, None)
+command: vaultcontainer_find
+args: 1,9,4
+arg: Str('criteria?', noextrawhitespace=False)
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('cn', attribute=True, autofill=False, cli_name='container_name',
maxlength=255, multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=False)
+option: Str('description', attribute=True, autofill=False, cli_name='desc',
multivalue=False, query=True, required=False)
+option: Str('parent', attribute=False, autofill=False, cli_name='parent',
multivalue=False, query=True, required=False)
+option: Flag('pkey_only?', autofill=True, default=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Str('version?', exclude='webui')
+output: Output('count', <type 'int'>, None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A
list of LDAP entries', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('truncated', <type 'bool'>, None)
+command: vaultcontainer_mod
+args: 1,9,3
+arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
primary_key=True, query=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False,
exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Str('description', attribute=True, autofill=False, cli_name='desc',
multivalue=False, required=False)
+option: Str('parent', attribute=False, autofill=False, cli_name='parent',
multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+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: vaultcontainer_show
+args: 1,5,3
+arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255,
multivalue=False,
pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[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('continue', autofill=True, cli_name='continue', default=False)
+option: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False,
exclude='webui')
+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
diff --git a/VERSION b/VERSION
index
b0d41e5e1ec59ddefbdcccf588b97bac2ff798ee..fe23eae5f349f4a2d40c3d3e55f6168a82b961b2
100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=108
-# Last change: pvoborni - manage authorization of keytab operations
+IPA_API_VERSION_MINOR=109
+# Last change: edewata - initial vault implementation
diff --git a/install/share/60basev4.ldif b/install/share/60basev4.ldif
new file mode 100644
index
0000000000000000000000000000000000000000..97553d53938093c1b0ecba0826fc469d0d758c62
--- /dev/null
+++ b/install/share/60basev4.ldif
@@ -0,0 +1,3 @@
+dn: cn=schema
+objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' SUP nsContainer
STRUCTURAL MAY ( description ) X-ORIGIN 'IPA v4.1' )
+objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' SUP
nsContainer STRUCTURAL MAY ( description ) X-ORIGIN 'IPA v4.1' )
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index
878d8868bbbb4f774d378b1d2e886841e2b4b7e4..ec417b634aeb80be3a39f2b8e3410e68a1131ee0
100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -14,6 +14,7 @@ app_DATA = \
60ipaconfig.ldif \
60basev2.ldif \
60basev3.ldif \
+ 60basev4.ldif \
60ipadns.ldif \
60ipapk11.ldif \
61kerberos-ipav3.ldif \
diff --git a/install/share/copy-schema-to-ca.py
b/install/share/copy-schema-to-ca.py
index
fc53fe4cb52486cc618bec77aabe8283ad5eadbc..fb938d212f0f4ddd9a8250a362b89c29d3078efd
100755
--- a/install/share/copy-schema-to-ca.py
+++ b/install/share/copy-schema-to-ca.py
@@ -29,6 +29,7 @@ SCHEMA_FILENAMES = (
"60ipaconfig.ldif",
"60basev2.ldif",
"60basev3.ldif",
+ "60basev4.ldif",
"60ipadns.ldif",
"61kerberos-ipav3.ldif",
"65ipacertstore.ldif",
diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update
new file mode 100644
index
0000000000000000000000000000000000000000..59e5b629ce4e6c5acac06df78f02106afa6c859e
--- /dev/null
+++ b/install/updates/40-vault.update
@@ -0,0 +1,27 @@
+dn: cn=vaults,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
+default: cn: vaults
+default: description: Root vault container
+
+dn: cn=services,cn=vaults,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
+default: cn: services
+default: description: Services vault container
+
+dn: cn=shared,cn=vaults,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
+default: cn: shared
+default: description: Shared vault container
+
+dn: cn=users,cn=vaults,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
+default: cn: users
+default: description: Users vault container
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index
2bd877a0d89525a69ea2d09647499ab2311bb358..a127f91cbe03aa13ec90bd628eaa29b7a898c3b9
100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -32,6 +32,7 @@ app_DATA = \
40-dns.update \
40-automember.update \
40-otp.update \
+ 40-vault.update \
45-roles.update \
50-7_bit_check.update \
50-dogtag10-migration.update \
diff --git a/ipa-client/man/default.conf.5 b/ipa-client/man/default.conf.5
index
dbc8a5b4647439de4de7c01152d098eb0561e236..0973f1a07179ad64daa326a02803cdc9ba1870aa
100644
--- a/ipa-client/man/default.conf.5
+++ b/ipa-client/man/default.conf.5
@@ -221,6 +221,7 @@ The following define the containers for the IPA server.
Containers define where
container_sudocmdgroup: cn=sudocmdgroups,cn=sudo
container_sudorule: cn=sudorules,cn=sudo
container_user: cn=users,cn=accounts
+ container_vault: cn=vaults
container_virtual: cn=virtual operations,cn=etc
.SH "FILES"
diff --git a/ipalib/constants.py b/ipalib/constants.py
index
325414b64fdacd4d8df261588cfc9b7481923be1..f64e02b5cf2a949a3c0ea7c1702132a3a09c1c19
100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -97,6 +97,7 @@ DEFAULT_CONFIG = (
('container_hbacservice', DN(('cn', 'hbacservices'), ('cn', 'hbac'))),
('container_hbacservicegroup', DN(('cn', 'hbacservicegroups'), ('cn',
'hbac'))),
('container_dns', DN(('cn', 'dns'))),
+ ('container_vault', DN(('cn', 'vaults'))),
('container_virtual', DN(('cn', 'virtual operations'), ('cn', 'etc'))),
('container_sudorule', DN(('cn', 'sudorules'), ('cn', 'sudo'))),
('container_sudocmd', DN(('cn', 'sudocmds'), ('cn', 'sudo'))),
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index
e206289248dfe9ae79bd87271ff2c7672fb98b4f..928a996e35cf31d21e6e85e4ea31380ec9b2c2ce
100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -22,6 +22,7 @@ from time import gmtime, strftime
import string
import posixpath
import os
+import traceback
from ipalib import api, errors
from ipalib import Flag, Int, Password, Str, Bool, StrEnum, DateTime
@@ -889,6 +890,14 @@ class user_del(LDAPDelete):
else:
self.api.Command.otptoken_del(token)
+ # Delete user's private vault container.
+ try:
+ vaultcontainer_id =
self.api.Object.vaultcontainer.get_private_id(owner)
+ (vaultcontainer_parent_id, vaultcontainer_name) =
self.api.Object.vaultcontainer.split_id(vaultcontainer_id)
+ self.api.Command.vaultcontainer_del(vaultcontainer_name,
parent=vaultcontainer_parent_id)
+ except errors.NotFound:
+ pass
+
return dn
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
new file mode 100644
index
0000000000000000000000000000000000000000..c0e66621d1cf326b0bfb88565951d66cda3b9500
--- /dev/null
+++ b/ipalib/plugins/vault.py
@@ -0,0 +1,724 @@
+# Authors:
+# Endi S. Dewata <edew...@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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 json
+import os
+import random
+import shutil
+import string
+import tempfile
+
+import pki
+import pki.account
+import pki.crypto
+import pki.key
+
+from ipalib import api, errors
+from ipalib import Str, Bytes
+from ipalib.plugable import Registry
+from ipalib.plugins.baseldap import *
+from ipalib.plugins import baseldap
+from ipalib.request import context
+from ipalib.plugins.user import split_principal
+from ipalib import _, ngettext
+from ipaplatform.paths import paths
+import ipapython.nsslib
+
+__doc__ = _("""
+Vaults
+
+Manage vaults and vault containers.
+
+EXAMPLES:
+
+ List private vaults:
+ ipa vault-find
+
+ List shared vaults:
+ ipa vault-find --parent /shared
+
+ Add a vault:
+ ipa vault-add MyVault
+
+ Show a vault:
+ ipa vault-show MyVault
+
+ Archive a secret:
+ ipa vault-archive MyVault --in secret.in
+
+ Retrieve a secret:
+ ipa vault-retrieve MyVault --out secret.out
+
+ Delete a vault:
+ ipa vault-del MyVault
+
+ List private vault containers:
+ ipa vaultcontainer-find
+
+ List top-level vault containers:
+ ipa vaultcontainer-find --parent /
+
+ Add a vault container:
+ ipa vaultcontainer-add MyContainer
+
+ Show a vault container:
+ ipa vaultcontainer-show MyContainer
+
+ Delete a vault container:
+ ipa vaultcontainer-del MyContainer
+""")
+
+register = Registry()
+transport_cert_nickname = "KRA Transport Certificate"
+
+@register()
+class vaultcontainer(LDAPObject):
+ """
+ Vault container object.
+ """
+ container_dn = api.env.container_vault
+ object_name = _('vault container')
+ object_name_plural = _('vault containers')
+
+ object_class = ['ipaVaultContainer']
+ default_attributes = [
+ 'cn', 'description',
+ ]
+ search_display_attributes = [
+ 'cn', 'description',
+ ]
+
+ label = _('Vault Containers')
+ label_singular = _('Vault Container')
+
+ takes_params = (
+ Str('cn',
+ pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
+ pattern_errmsg='may only include letters, numbers, _, -, . and $',
+ maxlength=255,
+ cli_name='container_name',
+ label=_('Container name'),
+ primary_key=True,
+ ),
+ Str('description?',
+ cli_name='desc',
+ label=_('Description'),
+ doc=_('Container description'),
+ ),
+ Str('parent?',
+ cli_name='parent',
+ label=_('Parent'),
+ doc=_('Parent container'),
+ flags=('virtual_attribute'),
+ ),
+ )
+
+ def get_private_id(self, username=None):
+ """
+ Returns user's private container ID (i.e. /users/<username>/).
+ """
+
+ if not username:
+ principal = getattr(context, 'principal')
+ (username, realm) = split_principal(principal)
+
+ return u'/users/' + username + u'/'
+
+ def normalize_id(self, id):
+ """
+ Normalizes container ID.
+ """
+
+ # if ID is empty, return user's private container ID
+ if not id:
+ return self.get_private_id()
+
+ # make sure ID ends with slash
+ if not id.endswith(u'/'):
+ id += u'/'
+
+ # if it's an absolute ID, do nothing
+ if id.startswith(u'/'):
+ return id
+
+ # otherwise, prepend with user's private container ID
+ return self.get_private_id() + id
+
+ def split_id(self, id):
+ """
+ Split a normalized container ID into (parent ID, container name) tuple.
+ """
+
+ # handle root ID
+ if id == u'/':
+ return (None, None)
+
+ # split ID into parent ID, container name, and empty string
+ parts = id.rsplit(u'/', 2)
+
+ # return parent ID and container name
+ return (parts[-3] + u'/', parts[-2])
+
+ def get_dn(self, *keys, **kwargs):
+ """
+ Generate vault container DN.
+ """
+
+ id = keys[0]
+ dn = DN(self.container_dn, api.env.basedn)
+
+ # if ID is not specified, return base DN
+ if not id:
+ return dn
+
+ # for each name in the ID, prepend the base DN
+ for name in id.split(u'/'):
+ if name:
+ dn = DN(('cn', name), dn)
+
+ return dn
+
+
+@register()
+class vaultcontainer_add(LDAPCreate):
+ __doc__ = _('Create a new vault container.')
+
+ msg_summary = _('Added vault container "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
**options):
+
+ name = entry_attrs.get('cn')
+ parent_id = self.obj.normalize_id(options.get('parent'))
+
+ # add parent container if it doesn't exist
+ try:
+ (grandparent_id, parent_name) = self.obj.split_id(parent_id)
+ if parent_name:
+ api.Command.vaultcontainer_add(parent_name,
parent=grandparent_id)
+ except errors.DuplicateEntry:
+ pass
+
+ id = parent_id + name
+ dn = self.obj.get_dn(id)
+
+ return dn
+
+
+@register()
+class vaultcontainer_del(LDAPDelete):
+ __doc__ = _('Delete a vault container.')
+
+ msg_summary = _('Deleted vault container "%(value)s"')
+
+ takes_options = LDAPDelete.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ )
+
+ def pre_callback(self, ldap, dn, *keys, **options):
+
+ name = keys[0]
+ parent_id = self.obj.normalize_id(options.get('parent'))
+ id = parent_id + name
+ dn = self.obj.get_dn(id)
+ return dn
+
+
+@register()
+class vaultcontainer_find(LDAPSearch):
+ __doc__ = _('Search for vault containers.')
+
+ msg_summary = ngettext(
+ '%(count)d vault container matched', '%(count)d vault containers
matched', 0
+ )
+
+ def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys,
**options):
+
+ parent_id = self.obj.normalize_id(options.get('parent'))
+ base_dn = self.obj.get_dn(parent_id)
+ return (filter, base_dn, scope)
+
+
+@register()
+class vaultcontainer_mod(LDAPUpdate):
+ __doc__ = _('Modify a vault container.')
+
+ msg_summary = _('Modified vault container "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[1]
+ parent_id = self.obj.normalize_id(options.get('parent'))
+ id = parent_id + name
+ dn = self.obj.get_dn(id)
+ return dn
+
+
+@register()
+class vaultcontainer_show(LDAPRetrieve):
+ __doc__ = _('Display information about a vault container.')
+
+ takes_options = LDAPDelete.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ )
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[0]
+ parent_id = self.obj.normalize_id(options.get('parent'))
+ id = parent_id + name
+ dn = self.obj.get_dn(id)
+ return dn
+
+
+@register()
+class vault(LDAPObject):
+ """
+ Vault object.
+ """
+ container_dn = api.env.container_vault
+ object_name = _('vault')
+ object_name_plural = _('vaults')
+
+ object_class = ['ipaVault']
+ default_attributes = [
+ 'cn', 'description',
+ ]
+ search_display_attributes = [
+ 'cn', 'description',
+ ]
+
+ label = _('Vaults')
+ label_singular = _('Vault')
+
+ takes_params = (
+ Str('cn',
+ pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
+ pattern_errmsg='may only include letters, numbers, _, -, . and $',
+ maxlength=255,
+ cli_name='vault_name',
+ label=_('Vault name'),
+ primary_key=True,
+ ),
+ Str('description?',
+ cli_name='desc',
+ label=_('Description'),
+ doc=_('Vault description'),
+ ),
+ Bytes('secret?',
+ cli_name='secret',
+ label=_('Secret'),
+ doc=_('Secret'),
+ ),
+ Str('parent?',
+ cli_name='parent',
+ label=_('Parent'),
+ doc=_('Parent container'),
+ flags=('virtual_attribute'),
+ ),
+ )
+
+ def get_dn(self, *keys, **kwargs):
+ """
+ Generate vault DN.
+ """
+
+ name = keys[0]
+ parent_id =
api.Object.vaultcontainer.normalize_id(kwargs.get('parent'))
+ parent_dn = api.Object.vaultcontainer.get_dn(parent_id)
+ dn = DN(('cn', name), parent_dn)
+
+ return dn
+
+
+@register()
+class vault_add(LDAPCreate):
+ __doc__ = _('Create a new vault.')
+
+ takes_options = LDAPRetrieve.takes_options + (
+ Str('in?',
+ cli_name='in',
+ doc=_('Input file containing the secret'),
+ ),
+ )
+
+ msg_summary = _('Added vault "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
**options):
+
+ name = entry_attrs.get('cn')
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(name, parent=parent_id)
+
+ # add parent container if it doesn't exist
+ try:
+ (grandparent_id, parent_name) =
api.Object.vaultcontainer.split_id(parent_id)
+ if parent_name:
+ api.Command.vaultcontainer_add(parent_name,
parent=grandparent_id)
+ except errors.DuplicateEntry:
+ pass
+
+ return dn
+
+ def forward(self, *args, **options):
+
+ vault_id = args[0]
+
+ secret = options.get('secret')
+ file = options.get('in')
+
+ # don't send these parameters to server
+ if 'secret' in options:
+ del options['secret']
+ if 'in' in options:
+ del options['in']
+
+ if secret:
+ pass
+
+ elif file:
+ with open(file) as f:
+ secret = f.read()
+
+ # create the vault
+ response = super(vault_add, self).forward(*args, **options)
+
+ # archive an empty secret
+ api.Command.vault_archive(vault_id, secret=secret)
+
+ return response
+
+
+@register()
+class vault_del(LDAPDelete):
+ __doc__ = _('Delete a vault.')
+
+ msg_summary = _('Deleted vault "%(value)s"')
+
+ takes_options = LDAPDelete.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ )
+
+ def pre_callback(self, ldap, dn, *keys, **options):
+
+ vault_id = keys[0]
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(vault_id, parent=parent_id)
+
+
+ kra_client = api.Backend.kra.get_client()
+
+ kra_account = pki.account.AccountClient(kra_client.connection)
+ kra_account.login()
+
+ client_key_id = 'ipa-' + vault_id
+
+ try:
+ key_info = kra_client.keys.get_active_key_info(client_key_id)
+
+ kra_client.keys.modify_key_status(
+ key_info.get_key_id(),
+ pki.key.KeyClient.KEY_STATUS_INACTIVE)
+
+ except pki.ResourceNotFoundException, e:
+ pass
+
+ return dn
+
+
+@register()
+class vault_find(LDAPSearch):
+ __doc__ = _('Search for vaults.')
+
+ msg_summary = ngettext(
+ '%(count)d vault matched', '%(count)d vaults matched', 0
+ )
+
+ def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys,
**options):
+
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ base_dn = api.Object.vaultcontainer.get_dn(parent_id)
+ return (filter, base_dn, scope)
+
+
+@register()
+class vault_mod(LDAPUpdate):
+ __doc__ = _('Modify a vault.')
+
+ msg_summary = _('Modified vault "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[1]
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(name, parent=parent_id)
+ return dn
+
+
+@register()
+class vault_show(LDAPRetrieve):
+ __doc__ = _('Display information about a vault.')
+
+ takes_options = LDAPRetrieve.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ )
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[0]
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(name, parent=parent_id)
+ return dn
+
+
+@register()
+class vault_archive(LDAPRetrieve):
+ __doc__ = _('Archive a secret into a vault.')
+
+ takes_options = LDAPRetrieve.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ Bytes('secret?',
+ cli_name='secret',
+ doc=_('Secret to archive'),
+ ),
+ Str('in?',
+ cli_name='in',
+ doc=_('Input file containing the secret'),
+ ),
+ Bytes('wrapped_session_key?',
+ cli_name='wrapped_session_key',
+ doc=_('Session key wrapped with transport certificate and encoded
in base-64'),
+ ),
+ Bytes('encrypted_data?',
+ cli_name='encrypted_data',
+ doc=_('Data encrypted with session key and encoded in base-64'),
+ ),
+ Bytes('nonce?',
+ cli_name='nonce',
+ doc=_('Nonce encrypted encoded in base-64'),
+ ),
+ )
+
+ msg_summary = _('Archived secret into vault "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[0]
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(name, parent=parent_id)
+ return dn
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ vault_id = entry_attrs.get('cn')[0]
+
+ kra_client = api.Backend.kra.get_client()
+
+ kra_account = pki.account.AccountClient(kra_client.connection)
+ kra_account.login()
+
+ client_key_id = 'ipa-' + vault_id
+
+ try:
+ key_info = kra_client.keys.get_active_key_info(client_key_id)
+
+ kra_client.keys.modify_key_status(
+ key_info.get_key_id(),
+ pki.key.KeyClient.KEY_STATUS_INACTIVE)
+
+ except pki.ResourceNotFoundException, e:
+ pass
+
+ wrapped_session_key = base64.b64decode(options['wrapped_session_key'])
+ encrypted_data = base64.b64decode(options['encrypted_data'])
+ nonce = base64.b64decode(options['nonce'])
+
+ kra_client.keys.archive_encrypted_data(
+ client_key_id,
+ pki.key.KeyClient.PASS_PHRASE_TYPE,
+ encrypted_data,
+ wrapped_session_key,
+ "{1 2 840 113549 3 7}",
+ base64.encodestring(nonce),
+ )
+
+ kra_account.logout()
+
+ return dn
+
+ def forward(self, *args, **options):
+
+ vault_id = args[0]
+
+ secret = options.get('secret')
+ file = options.get('in')
+
+ # don't send these parameters to server
+ if 'secret' in options:
+ del options['secret']
+ if 'in' in options:
+ del options['in']
+
+ if secret:
+ pass
+
+ elif file:
+ with open(file) as f:
+ secret = f.read()
+
+ else:
+ secret = ''
+
+ crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR)
+ crypto.initialize()
+ ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR
+
+ nonce = crypto.generate_nonce_iv()
+ session_key = crypto.generate_session_key()
+ nss_transport_cert = crypto.get_cert(transport_cert_nickname)
+
+ wrapped_session_key = crypto.asymmetric_wrap(
+ session_key,
+ nss_transport_cert
+ )
+
+ encrypted_data = crypto.symmetric_wrap(
+ secret,
+ session_key,
+ nonce_iv=nonce
+ )
+
+ options['wrapped_session_key'] = base64.b64encode(wrapped_session_key)
+ options['encrypted_data'] = base64.b64encode(encrypted_data)
+ options['nonce'] = base64.b64encode(nonce)
+
+ return super(vault_archive, self).forward(*args, **options)
+
+
+@register()
+class vault_retrieve(LDAPRetrieve):
+ __doc__ = _('Retrieve a secret from a vault.')
+
+ takes_options = LDAPRetrieve.takes_options + (
+ Str('parent?',
+ cli_name='parent',
+ doc=_('Parent container'),
+ ),
+ Str('out?',
+ cli_name='out',
+ doc=_('Output file to store the secret'),
+ ),
+ Bytes('wrapped_session_key?',
+ cli_name='wrapped_session_key',
+ doc=_('Session key wrapped with transport certificate and encoded
in base-64'),
+ ),
+ )
+
+ msg_summary = _('Retrieved secret from vault "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ name = keys[0]
+ parent_id =
api.Object.vaultcontainer.normalize_id(options.get('parent'))
+ dn = self.obj.get_dn(name, parent=parent_id)
+ return dn
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+
+ vault_id = entry_attrs.get('cn')[0]
+
+ wrapped_session_key = base64.b64decode(options['wrapped_session_key'])
+
+ client_key_id = 'ipa-' + vault_id
+
+ kra_client = api.Backend.kra.get_client()
+
+ kra_account = pki.account.AccountClient(kra_client.connection)
+ kra_account.login()
+
+ key_client = kra_client.keys
+ key_info = key_client.get_active_key_info(client_key_id)
+
+ key = key_client.retrieve_key(
+ key_info.get_key_id(),
+ wrapped_session_key)
+
+ entry_attrs['encrypted_data'] = base64.b64encode(key.encrypted_data)
+ entry_attrs['nonce'] = base64.b64encode(key.nonce_data)
+
+ kra_account.logout()
+
+ return dn
+
+ def forward(self, *args, **options):
+
+ vault_id = args[0]
+
+ file = options.get('out')
+
+ # don't send these parameters to server
+ if 'out' in options:
+ del options['out']
+
+ crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR)
+ crypto.initialize()
+ ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR
+
+ session_key = crypto.generate_session_key()
+ nss_transport_cert = crypto.get_cert(transport_cert_nickname)
+
+ wrapped_session_key = crypto.asymmetric_wrap(
+ session_key,
+ nss_transport_cert
+ )
+
+ options['wrapped_session_key'] = base64.b64encode(wrapped_session_key)
+
+ response = super(vault_retrieve, self).forward(*args, **options)
+
+ encrypted_data = base64.b64decode(response['result']['encrypted_data'])
+ nonce = base64.b64decode(response['result']['nonce'])
+
+ secret = crypto.symmetric_unwrap(
+ encrypted_data,
+ session_key,
+ nonce_iv=nonce)
+
+ if file:
+ with open(file, 'w') as f:
+ f.write(secret)
+
+ else:
+ response['result']['secret'] = unicode(secret)
+
+ return response
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index
d1569697cba7d7fb9f44a3b85afb643a42624f20..9fa736dcf635b286035b4438a6c342e64f09d1d6
100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -56,6 +56,7 @@ IPA_SCHEMA_FILES = ("60kerberos.ldif",
"60ipaconfig.ldif",
"60basev2.ldif",
"60basev3.ldif",
+ "60basev4.ldif",
"60ipapk11.ldif",
"60ipadns.ldif",
"61kerberos-ipav3.ldif",
--
1.9.0
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel