backup: fixed decryption during backup restoration process

This commit is contained in:
Olivier MEDOC 2013-09-30 10:26:04 +02:00
parent c2645bcb92
commit 1a80893ef0

View File

@ -1302,7 +1302,7 @@ def restore_vm_dirs (backup_dir, backup_tmpdir, passphrase, vms_dirs, vms, vms_s
# Setup worker to extract encrypted data chunks to the restore dirs
from multiprocessing import Queue,Process
class Extract_Worker(Process):
def __init__(self,queue,base_dir,passphrase,encrypted,total_size,print_callback,error_callback,progress_callback):
def __init__(self,queue,base_dir,passphrase,encrypted,total_size,print_callback,error_callback,progress_callback,vmproc=None):
super(Extract_Worker, self).__init__()
self.queue = queue
self.base_dir = base_dir
@ -1316,6 +1316,8 @@ def restore_vm_dirs (backup_dir, backup_tmpdir, passphrase, vms_dirs, vms, vms_s
self.error_callback = error_callback
self.progress_callback = progress_callback
self.vmproc = vmproc
self.restore_pipe = os.path.join(self.base_dir,"restore_pipe")
print "Creating pipe in:",self.restore_pipe
print os.mkfifo(self.restore_pipe)
@ -1344,14 +1346,17 @@ def restore_vm_dirs (backup_dir, backup_tmpdir, passphrase, vms_dirs, vms, vms_s
self.tar2_command = subprocess.Popen(self.tar2_command,stdin=subprocess.PIPE)
pipe = open(self.restore_pipe,'r+b')
if self.encrypted:
# Start decrypt
encryptor = subprocess.Popen (["openssl", "enc", "-d", "-aes-256-cbc", "-pass", "pass:"+passphrase], stdin=open(filename,'rb'), stdout=pipe)
if self.encrypted:
# Start decrypt
encryptor = subprocess.Popen (["openssl", "enc", "-d", "-aes-256-cbc", "-pass", "pass:"+passphrase], stdin=open(filename,'rb'), stdout=subprocess.PIPE)
# progress_callback, in_stream, streamproc, backup_target, total_backup_sz, hmac=None, vmproc=None, addproc=None, remove_trailing_bytes=0):
run_error = wait_backup_feedback(self.compute_progress, pipe, encryptor, self.tar2_command.stdin, self.total_size, hmac=None, vmproc=None, addproc=self.tar2_command)
else:
run_error = wait_backup_feedback(self.compute_progress, open(filename,"rb"), None, pipe, self.total_size, hmac=None, vmproc=None, addproc=self.tar2_command)
run_error = wait_backup_feedback(self.compute_progress, encryptor.stdout, encryptor, pipe, self.total_size, hmac=None, vmproc=self.vmproc, addproc=self.tar2_command)
#print "End wait_backup_feedback",run_error,self.tar2_command.poll(),encryptor.poll()
else:
run_error = wait_backup_feedback(self.compute_progress, open(filename,"rb"), None, pipe, self.total_size, hmac=None, vmproc=self.vmproc, addproc=self.tar2_command)
pipe.close()
self.print_callback("Run error:"+run_error)
self.print_callback(str(self.tar2_command.poll()))
@ -1365,7 +1370,6 @@ def restore_vm_dirs (backup_dir, backup_tmpdir, passphrase, vms_dirs, vms, vms_s
else:
self.print_callback("Releasing next chunck")
pipe.close()
self.tar2_command.stdin.write("\n")
# Delete the file as we don't need it anymore
@ -1484,6 +1488,8 @@ def backup_restore_header(restore_target, passphrase, encrypt=False, appvm=None)
feedback_file = tempfile.NamedTemporaryFile()
backup_tmpdir = tempfile.mkdtemp(prefix="/var/tmp/restore_")
os.chdir(backup_tmpdir)
# Tar with tapelength does not deals well with stdout (close stdout between two tapes)
# For this reason, we will use named pipes instead
print "Working in",backup_tmpdir
@ -1507,12 +1513,6 @@ def backup_restore_header(restore_target, passphrase, encrypt=False, appvm=None)
vmproc = vm.run(command = restore_command, passio_popen = True)
vmproc.stdin.write(restore_target.replace("\r","").replace("\n","")+"\n")
headers = vmproc.stdout.read(4096*64)
vmproc.terminate()
if len(headers) <= 0:
raise QubesException("ERROR: unable to read the backup target {0}".format(restore_target))
else:
# Create the target directory
if not os.path.exists (restore_target):
@ -1521,37 +1521,53 @@ def backup_restore_header(restore_target, passphrase, encrypt=False, appvm=None)
fp = open(restore_target,'rb')
headers = fp.read(4096*16)
command = subprocess.Popen(['tar', '-i', '-xv', '-C', backup_tmpdir, 'qubes.xml.*'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
headers,stderr = command.communicate(headers)
if len(headers) <= 0:
print stderr
raise QubesException("ERROR: unable to read the qubes backup file {0}. Is it really a backup?".format(restore_target))
print "Retrieved headers",headers
command = subprocess.Popen(['tar', '-i', '-xv', '-C', backup_tmpdir, 'qubes.xml*'],stdin=vmproc.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
for filename in headers.splitlines():
print "Loading hmac for file",filename
hmac = load_hmac(open(os.path.join(backup_tmpdir,filename+".hmac"),'r').read())
filename = command.stdout.readline().strip(" \t\r\n")
print "Getting file",filename
print "Verifying file",filename
hmac_proc = subprocess.Popen (["openssl", "dgst", "-hmac", passphrase], stdin=open(os.path.join(backup_tmpdir,filename),'rb'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout,stderr = hmac_proc.communicate()
if len(stderr) > 0:
raise QubesException("ERROR: verify file {0}: {1}".format((filename,stderr)))
else:
if len(hmac) > 0 and load_hmac(stdout) == hmac:
print "File verification OK -> Extracting archive",filename
# FIXME: handle encrypted file
command = subprocess.Popen(['tar', '-xvf', os.path.join(backup_tmpdir,filename), '-C', backup_tmpdir],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
headers,stderr = command.communicate(headers)
if len(headers) <= 0:
print stderr
raise QubesException("ERROR: unable to read the qubes backup file {0}. Is it really a backup?".format(restore_target))
else:
return (backup_tmpdir,headers.strip(" \r\n\t"))
hmacfile = command.stdout.readline().strip(" \t\r\n")
print "Getting hmac",hmacfile
while not os.path.exists(os.path.join(backup_tmpdir,hmacfile)):
time.sleep(1000)
command.terminate()
command.wait()
vmproc.terminate()
vmproc.wait()
print "Loading hmac for file",filename
hmac = load_hmac(open(os.path.join(backup_tmpdir,filename+".hmac"),'r').read())
print "Successfully retrieved headers"
print "Verifying file",filename
hmac_proc = subprocess.Popen (["openssl", "dgst", "-hmac", passphrase], stdin=open(os.path.join(backup_tmpdir,filename),'rb'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout,stderr = hmac_proc.communicate()
if len(stderr) > 0:
raise QubesException("ERROR: verify file {0}: {1}".format((filename,stderr)))
else:
if len(hmac) > 0 and load_hmac(stdout) == hmac:
print "File verification OK -> Extracting archive",filename
if encrypt:
encryptor = subprocess.Popen (["openssl", "enc", "-d", "-aes-256-cbc", "-pass", "pass:"+passphrase], stdin=open(os.path.join(backup_tmpdir,filename),'rb'), stdout=subprocess.PIPE)
tarhead_command = subprocess.Popen(['tar', '--tape-length','1000000', '-xv'],stdin=encryptor.stdout)
else:
raise QubesException("ERROR: invalid hmac for file {0}: {1}. Is the passphrase correct?".format(filename,load_hmac(stdout)))
encryptor = None
tarhead_command = subprocess.Popen(['tar', '--tape-length','1000000', '-xvf', os.path.join(backup_tmpdir,filename)])
tarhead_command.wait()
if encryptor:
if encryptor.poll() != 0:
raise QubesException("ERROR: unable to decrypt file {0}".format(filename))
if tarhead_command.poll() != 0:
raise QubesException("ERROR: unable to extract the qubes.xml file. Is archive encrypted?")
return (backup_tmpdir,"qubes.xml")
else:
raise QubesException("ERROR: unable to verify the qubes.xml file. Is the passphrase correct?")
return None
@ -1653,7 +1669,8 @@ def backup_restore_prepare(backup_dir, qubes_xml, passphrase, options = {}, host
# Maybe the (custom) netvm is in the backup?
netvm_on_backup = backup_collection.get_vm_by_name (netvm_name)
if not ((netvm_on_backup is not None) and netvm_on_backup.is_netvm() and is_vm_included_in_backup(backup_dir, netvm_on_backup)):
if not ((netvm_on_backup is not None) and netvm_on_backup.is_netvm() and is_vm_
included_in_backup(backup_dir, netvm_on_backup)):
if options['use-default-netvm']:
vms_to_restore[vm.name]['netvm'] = host_collection.get_default_netvm().name
vm.uses_default_netvm = True