summaryrefslogtreecommitdiffstats
path: root/src/sbin/bcfg2-crypt
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2012-09-13 10:39:40 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2012-09-13 10:39:40 -0400
commit91167b2c01e5a0abafd0c111fa3546601e1f5cf0 (patch)
treed907db32c9f3d37896846fe3e8c4aeddedbf5acf /src/sbin/bcfg2-crypt
parente2355c04d5b79866eb9ca26f1303cbf5fa36b757 (diff)
downloadbcfg2-91167b2c01e5a0abafd0c111fa3546601e1f5cf0.tar.gz
bcfg2-91167b2c01e5a0abafd0c111fa3546601e1f5cf0.tar.bz2
bcfg2-91167b2c01e5a0abafd0c111fa3546601e1f5cf0.zip
bcfg2-crypt: added -I option, made --stdout better, updated man page
Diffstat (limited to 'src/sbin/bcfg2-crypt')
-rwxr-xr-xsrc/sbin/bcfg2-crypt174
1 files changed, 118 insertions, 56 deletions
diff --git a/src/sbin/bcfg2-crypt b/src/sbin/bcfg2-crypt
index cb1b956fb..1af1771cf 100755
--- a/src/sbin/bcfg2-crypt
+++ b/src/sbin/bcfg2-crypt
@@ -3,16 +3,18 @@
import os
import sys
+import copy
import logging
import lxml.etree
import Bcfg2.Logger
import Bcfg2.Options
+from Bcfg2.Server import XMLParser
+from Bcfg2.Compat import input
try:
import Bcfg2.Encryption
except ImportError:
err = sys.exc_info()[1]
- print("Import failed '%s'. Is M2Crypto installed?" %
- err)
+ print("Could not import %s. Is M2Crypto installed?" % err)
raise SystemExit(1)
LOGGER = None
@@ -103,7 +105,8 @@ class Encryptor(object):
self.logger.error("Error reading %s, skipping: %s" % (fname, err))
return False
- self.set_passphrase()
+ if not self.set_passphrase():
+ return False
crypted = []
try:
@@ -119,22 +122,7 @@ class Encryptor(object):
self.logger.error("Error getting data to encrypt from %s: %s" %
(fname, err))
return False
-
- new_fname = self.get_encrypted_filename(fname)
- try:
- open(new_fname, "wb").write(self.unchunk(crypted, plaintext))
- self.logger.info("Wrote encrypted data to %s" % new_fname)
- return True
- except IOError:
- err = sys.exc_info()[1]
- self.logger.error("Error writing encrypted data from %s to %s: %s" %
- (fname, new_fname, err))
- return False
- except EncryptionChunkingError:
- err = sys.exc_info()[1]
- self.logger.error("Error assembling encrypted data from %s: %s" %
- (fname, err))
- return False
+ return self.unchunk(crypted, plaintext)
def _encrypt(self, plaintext, passphrase, name=None):
return Bcfg2.Encryption.ssl_encrypt(plaintext, passphrase)
@@ -200,6 +188,25 @@ class Encryptor(object):
(fname, err))
return False
+ def write_encrypted(self, fname, data=None):
+ if data is None:
+ data = self.decrypt(fname)
+ new_fname = self.get_encrypted_filename(fname)
+ try:
+ open(new_fname, "wb").write(data)
+ self.logger.info("Wrote encrypted data to %s" % new_fname)
+ return True
+ except IOError:
+ err = sys.exc_info()[1]
+ self.logger.error("Error writing encrypted data from %s to %s: %s" %
+ (fname, new_fname, err))
+ return False
+ except EncryptionChunkingError:
+ err = sys.exc_info()[1]
+ self.logger.error("Error assembling encrypted data from %s: %s" %
+ (fname, err))
+ return False
+
def write_decrypted(self, fname, data=None):
if data is None:
data = self.decrypt(fname)
@@ -262,12 +269,12 @@ class PropertiesEncryptor(Encryptor):
name = "true"
if plaintext.text and plaintext.text.strip():
plaintext.text = Bcfg2.Encryption.ssl_encrypt(plaintext.text,
- passphrase)
+ passphrase).strip()
plaintext.set("encrypted", name)
return plaintext
def chunk(self, data):
- xdata = lxml.etree.XML(data)
+ xdata = lxml.etree.XML(data, parser=XMLParser)
if self.setup['xpath']:
elements = xdata.xpath(self.setup['xpath'])
if not elements:
@@ -276,7 +283,28 @@ class PropertiesEncryptor(Encryptor):
else:
elements = xdata.xpath('//*[@encrypted]')
if not elements:
- elements = list(xdata.getiterator())
+ elements = list(xdata.getiterator(tag=lxml.etree.Element))
+
+ # filter out elements without text data
+ for el in elements[:]:
+ if not el.text:
+ elements.remove(el)
+
+ if self.setup['interactive']:
+ for element in elements[:]:
+ if len(element):
+ elt = copy.copy(element)
+ for child in elt.iterchildren():
+ elt.remove(child)
+ else:
+ elt = element
+ print(lxml.etree.tostring(
+ elt,
+ xml_declaration=False).decode("UTF-8").strip())
+ ans = input("Encrypt this element? [y/N] ")
+ if not ans.lower().startswith("y"):
+ elements.remove(element)
+
# this is not a good use of a generator, but we need to
# generate the full list of elements in order to ensure that
# some exist before we know what to return
@@ -291,7 +319,9 @@ class PropertiesEncryptor(Encryptor):
while xdata.getparent() != None:
xdata = xdata.getparent()
xdata.set("encryption", "true")
- return lxml.etree.tostring(xdata, xml_declaration=False).decode('UTF-8')
+ return lxml.etree.tostring(xdata,
+ xml_declaration=False,
+ pretty_print=True).decode('UTF-8')
def _get_passphrase(self, chunk):
pname = chunk.get("encrypted") or chunk.get("encryption")
@@ -304,13 +334,13 @@ class PropertiesEncryptor(Encryptor):
if not crypted.text or not crypted.text.strip():
self.logger.warning("Skipping empty element %s" % crypted.tag)
return crypted
- rv = Bcfg2.Encryption.ssl_decrypt(crypted.text, passphrase)
- crypted.text = rv
+ crypted.text = Bcfg2.Encryption.ssl_decrypt(crypted.text,
+ passphrase).strip()
return crypted
def main():
- optinfo = dict()
+ optinfo = dict(interactive=Bcfg2.Options.INTERACTIVE)
optinfo.update(Bcfg2.Options.CRYPT_OPTIONS)
optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
setup = Bcfg2.Options.OptionParser(optinfo)
@@ -321,18 +351,33 @@ def main():
if not setup['args']:
print(setup.hm)
raise SystemExit(1)
- elif setup['encrypt'] and (setup['decrypt_stdout'] or setup['decrypt']):
- print("You cannot specify both --encrypt and --decrypt or --stdout")
- raise SystemExit(1)
- elif setup['cfg'] and setup['properties']:
- print("You cannot specify both --cfg and --properties")
- raise SystemExit(1)
- elif setup['cfg'] and setup['properties']:
- print("Specifying --xpath with --cfg is nonsensical, ignoring --xpath")
- setup['xpath'] = Bcfg2.Options.CRYPT_XPATH.default
- elif setup['decrypt'] and setup['remove']:
- print("--remove cannot be used with --decrypt, ignoring")
- setup['remove'] = Bcfg2.Options.CRYPT_REMOVE.default
+
+ if setup['decrypt']:
+ if setup['encrypt']:
+ print("You cannot specify both --encrypt and --decrypt")
+ raise SystemExit(1)
+ elif setup['remove']:
+ print("--remove cannot be used with --decrypt, ignoring")
+ setup['remove'] = Bcfg2.Options.CRYPT_REMOVE.default
+ elif setup['interactive']:
+ print("Cannot decrypt interactively")
+ setup['interactive'] = False
+
+ if setup['cfg']:
+ if setup['properties']:
+ print("You cannot specify both --cfg and --properties")
+ raise SystemExit(1)
+ if setup['xpath']:
+ print("Specifying --xpath with --cfg is nonsensical, ignoring "
+ "--xpath")
+ setup['xpath'] = Bcfg2.Options.CRYPT_XPATH.default
+ if setup['interactive']:
+ print("You cannot use interactive mode with --cfg, ignoring -I")
+ setup['interactive'] = False
+ elif setup['properties']:
+ if setup['remove']:
+ print("--remove cannot be used with --properties, ignoring")
+ setup['remove'] = Bcfg2.Options.CRYPT_REMOVE.default
logger = get_logger(setup['verbose'])
@@ -369,32 +414,49 @@ def main():
if props:
encryptor = props_crypt
+ if setup['remove']:
+ logger.info("Cannot use --remove with Properties file %s, "
+ "ignoring for this file" % fname)
else:
+ if setup['xpath']:
+ logger.info("Cannot use xpath with Cfg file %s, ignoring xpath "
+ "for this file" % fname)
+ if setup['interactive']:
+ logger.info("Cannot use interactive mode with Cfg file %s, "
+ "ignoring -I for this file" % fname)
encryptor = cfg_crypt
+ data = None
if setup['encrypt']:
- if not encryptor.encrypt(fname):
- print("Failed to encrypt %s, skipping" % fname)
- elif setup['decrypt'] or setup['decrypt_stdout']:
- data = encryptor.decrypt(fname)
- if not data:
- print("Failed to decrypt %s, skipping" % fname)
- continue
- if setup['decrypt_stdout']:
- if len(setup['args']) > 1:
- print("----- %s -----" % fname)
- print(data)
- if len(setup['args']) > 1:
- print("")
- else:
- encryptor.write_decrypted(fname, data=data)
+ xform = encryptor.encrypt
+ write = encryptor.write_encrypted
+ elif setup['decrypt']:
+ xform = encryptor.decrypt
+ write = encryptor.write_decrypted
else:
logger.info("Neither --encrypt nor --decrypt specified, "
"determining mode")
- if not encryptor.decrypt(fname):
+ data = encryptor.decrypt(fname)
+ if data:
+ write = encryptor.write_decrypted
+ else:
logger.info("Failed to decrypt %s, trying encryption" % fname)
- if not encryptor.encrypt(fname):
- print("Failed to encrypt %s, skipping" % fname)
+ data = None
+ xform = encryptor.encrypt
+ write = encryptor.write_encrypted
+
+ if data is None:
+ data = xform(fname)
+ if not data:
+ print("Failed to %s %s, skipping" % (xform.__name__, fname))
+ if setup['crypt_stdout']:
+ if len(setup['args']) > 1:
+ print("----- %s -----" % fname)
+ print(data)
+ if len(setup['args']) > 1:
+ print("")
+ else:
+ write(fname, data=data)
if setup['remove'] and encryptor.get_encrypted_filename(fname) != fname:
try: