Browse code

2007-06-17 Michal Ludvig <michal@logix.cz>

* s3cmd: Added encryption key support to --configure



git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@125 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2007/06/16 22:12:45
Showing 5 changed files
... ...
@@ -1,5 +1,6 @@
1 1
 2007-06-17  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* s3cmd: Added encryption key support to --configure
3 4
 	* S3/PkgInfo.py: Bump up version to 0.9.4-pre
4 5
 	* setup.py: Cleaned up some rpm-specific stuff that 
5 6
 	  caused problems to Debian packager Mikhail Gusarov
... ...
@@ -1,3 +1,7 @@
1
+s3cmd 0.9.4   -   ...
2
+===========
3
+* Support for transparent GPG encryption of uploaded files.
4
+
1 5
 s3cmd 0.9.3   -   2007-05-26
2 6
 ===========
3 7
 * New command "du" for displaying size of your data in S3.
... ...
@@ -21,7 +21,7 @@ class Config(object):
21 21
 	acl_public = False
22 22
 	encrypt = False
23 23
 	gpg_passphrase = ""
24
-	gpg_command = "/usr/bin/gpg"
24
+	gpg_command = ""
25 25
 	gpg_encrypt = "%(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s"
26 26
 	gpg_decrypt = "%(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s"
27 27
 
... ...
@@ -9,6 +9,7 @@ import re
9 9
 import elementtree.ElementTree as ET
10 10
 import string
11 11
 import random
12
+import md5
12 13
 
13 14
 def parseNodes(nodes, xmlns = ""):
14 15
 	retval = []
... ...
@@ -104,3 +105,10 @@ def mktmpdir(prefix = "/tmp/tmpdir-", randchars = 10):
104 104
 def mktmpfile(prefix = "/tmp/tmpfile-", randchars = 20):
105 105
 	createfunc = lambda filename : os.close(os.open(filename, os.O_CREAT | os.O_EXCL))
106 106
 	return mktmpsomething(prefix, randchars, createfunc)
107
+
108
+def hash_file_md5(filename):
109
+	h = md5.new()
110
+	f = open(filename, "r")
111
+	h.update(f.read())
112
+	f.close()
113
+	return h.hexdigest()
... ...
@@ -13,6 +13,7 @@ from copy import copy
13 13
 from optparse import OptionParser, Option, OptionValueError, IndentedHelpFormatter
14 14
 from logging import debug, info, warning, error
15 15
 import elementtree.ElementTree as ET
16
+from distutils.spawn import find_executable
16 17
 
17 18
 ## Our modules
18 19
 from S3 import PkgInfo
... ...
@@ -250,7 +251,7 @@ def gpg_encrypt(filename):
250 250
 	code = gpg_command(command, cfg.gpg_passphrase)
251 251
 	return (code, tmp_filename, "gpg")
252 252
 
253
-def gpg_decrypt(filename, gpgenc_header = ""):
253
+def gpg_decrypt(filename, gpgenc_header = "", in_place = True):
254 254
 	tmp_filename = Utils.mktmpfile(filename)
255 255
 	args = {
256 256
 		"gpg_command" : cfg.gpg_command,
... ...
@@ -261,22 +262,28 @@ def gpg_decrypt(filename, gpgenc_header = ""):
261 261
 	info("Decrypting file %(input_file)s to %(output_file)s..." % args)
262 262
 	command = resolve_list(cfg.gpg_decrypt.split(" "), args)
263 263
 	code = gpg_command(command, cfg.gpg_passphrase)
264
-	if code == 0:
264
+	if code == 0 and in_place:
265 265
 		debug("Renaming %s to %s" % (tmp_filename, filename))
266 266
 		os.unlink(filename)
267 267
 		os.rename(tmp_filename, filename)
268
-	return (code)
268
+		tmp_filename = filename
269
+	return (code, tmp_filename)
269 270
 
270 271
 def run_configure(config_file):
271 272
 	cfg = Config()
272 273
 	options = [
273 274
 		("access_key", "Access Key", "Access key and Secret key are your identifiers for Amazon S3"),
274 275
 		("secret_key", "Secret Key"),
276
+		("gpg_passphrase", "Encryption password", "Encryption password is used to protect your files from reading\nby unauthorized persons while in transfer to S3"),
277
+		("gpg_command", "Path to GPG program"),
275 278
 		]
279
+	if getattr(cfg, "gpg_command") == "":
280
+		setattr(cfg, "gpg_command", find_executable("gpg"))
281
+
276 282
 	try:
277 283
 		while 1:
278 284
 			output("\nEnter new values or accept defaults in brackets with Enter.")
279
-			output("Refer to user manual for detailed description of all options.\n")
285
+			output("Refer to user manual for detailed description of all options.")
280 286
 			for option in options:
281 287
 				prompt = option[1]
282 288
 				try:
... ...
@@ -287,7 +294,7 @@ def run_configure(config_file):
287 287
 					pass
288 288
 
289 289
 				if len(option) >= 3:
290
-					output("%s" % option[2])
290
+					output("\n%s" % option[2])
291 291
 
292 292
 				val = raw_input(prompt + ": ")
293 293
 				if val != "":
... ...
@@ -300,12 +307,38 @@ def run_configure(config_file):
300 300
 				try:
301 301
 					output("Please wait...")
302 302
 					S3(Config()).bucket_list("", "")
303
-					output("\nSuccess. Your access key and secret key worked fine :-)")
304
-				except S3Error, e:
303
+					output("Success. Your access key and secret key worked fine :-)")
304
+
305
+					output("\nNow verifying that encryption works...")
306
+					if not getattr(cfg, "gpg_command"):
307
+						raise Exception("Path to GPG program not set")
308
+					if not os.path.isfile(getattr(cfg, "gpg_command")):
309
+						raise Exception("GPG program not found")
310
+					filename = Utils.mktmpfile()
311
+					f = open(filename, "w")
312
+					f.write(os.sys.copyright)
313
+					f.close()
314
+					ret_enc = gpg_encrypt(filename)
315
+					ret_dec = gpg_decrypt(ret_enc[1], ret_enc[2], False)
316
+					hash = [
317
+						Utils.hash_file_md5(filename),
318
+						Utils.hash_file_md5(ret_enc[1]),
319
+						Utils.hash_file_md5(ret_dec[1]),
320
+					]
321
+					os.unlink(filename)
322
+					os.unlink(ret_enc[1])
323
+					os.unlink(ret_dec[1])
324
+					if hash[0] == hash[2] and hash[0] != hash[1]:
325
+						output ("Success. Encryption and decryption worked fine :-)") 
326
+					else:
327
+						raise Exception("Encryption verification error.")
328
+
329
+				except Exception, e:
305 330
 					error("Test failed: %s" % (e))
306 331
 					val = raw_input("\nRetry configuration? [Y/n] ")
307 332
 					if val.lower().startswith("y") or val == "":
308 333
 						continue
334
+					
309 335
 
310 336
 			val = raw_input("\nSave settings? [y/N] ")
311 337
 			if val.lower().startswith("y"):