# password store management tool
-# Copyright (c) 2008 Peter Palfrader <peter@palfrader.org>
+# Copyright (c) 2008, 2009 Peter Palfrader <peter@palfrader.org>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
STDIN.reopen(inR)
STDOUT.reopen(outW)
STDERR.reopen(errW)
- exec(GNUPG, "--status-fd=#{statW.fileno}", *args)
+ begin
+ exec(GNUPG, "--status-fd=#{statW.fileno}", *args)
+ rescue Exception => e
+ outW.puts("[PWSEXECERROR]: #{e}")
+ exit(1)
+ end
raise ("Calling gnupg failed")
end
inR.close
throw "Unexpected pid: #{pid} vs #{wpid}" unless pid == wpid
throw "Process has not exited!?" unless status.exited?
throw "gpg call did not exit sucessfully" if (require_success and status.exitstatus != 0)
+ if m=/^\[PWSEXECERROR\]: (.*)/.match(outtxt) then
+ STDERR.puts "Could not run GnuPG: #{m[1]}"
+ exit(1)
+ end
return outtxt, stderrtxt, statustxt, status.exitstatus
end
end
def GnuPG.get_fprs_from_keyids(keyids)
learn_fingerprints_from_keyids(keyids)
- return keyids.collect{ |k| get_fpr_from_keyid(k) }
+ return keyids.collect{ |k| get_fpr_from_keyid(k) or "unknown" }
end
# this is to load the keys we will soon be asking about into
def GnuPG.learn_fingerprints_from_keyids(keyids)
need_to_learn = keyids.reject{ |k| @@keyid_fpr_mapping.has_key?(k) }
if need_to_learn.size > 0
- args = %w{--fast-list-mode --with-colons --with-fingerprint --list-keys}
+ # we can't use --fast-list-mode here because GnuPG is broken
+ # and does not show elmo's fingerprint in a call like
+ # gpg --with-colons --fast-list-mode --with-fingerprint --list-key D7C3F131AB2A91F5
+ args = %w{--with-colons --with-fingerprint --list-keys}
args.concat need_to_learn
(outtxt, stderrtxt, statustxt) = GnuPG.gpgcall('', args, true)
trusted.push line
end
- (outtxt, stderrtxt, statustxt, exitstatus) = GnuPG.gpgcall(content, %w{}, true)
+ (outtxt, stderrtxt, statustxt, exitstatus) = GnuPG.gpgcall(content, %w{})
goodsig = false
validsig = nil
statustxt.split("\n").each do |line|
end
if not goodsig
- STDERR.puts ".users file is not signed properly"
+ STDERR.puts ".users file is not signed properly. GnuPG said on stdout:"
+ STDERR.puts outtxt
+ STDERR.puts "and on stderr:"
+ STDERR.puts stderrtxt
+ STDERR.puts "and via statusfd:"
+ STDERR.puts statustxt
exit(1)
end
exit(1)
end
+ if not exitstatus==0
+ STDERR.puts "gpg verify failed for .users file"
+ exit(1)
+ end
+
return outtxt
end
group['keys'] = [] unless group['keys']
still_contains_groups = false
- group['members_to_do'].each do |member|
+ group['members_to_do'].clone.each do |member|
if is_group(member)
if @groups[member]['members_to_do'].size == 0
group['keys'].concat @groups[member]['keys']
end
return ok, fprs.uniq
end
+
+ def get_users()
+ return @users
+ end
end
class EncryptedFile
end
@accessible = true
@encrypted_content = File.read(filename)
- (outtxt, stderrtxt, statustxt) = GnuPG.gpgcall(@encrypted_content, %w{--with-colons --no-default-keyring --secret-keyring=/dev/null --keyring=/dev/null})
+ (outtxt, stderrtxt, statustxt) = GnuPG.gpgcall(@encrypted_content, %w{--with-colons --no-options --no-default-keyring --secret-keyring=/dev/null --keyring=/dev/null})
@encrypted = !(statustxt =~ /\[GNUPG:\] NODATA/)
if @encrypted
@readers = EncryptedFile.list_readers(statustxt)
def encrypt(content, recipients)
args = recipients.collect{ |r| "--recipient=#{r}"}
args.push "--trust-model=always"
+ args.push "--keyring=./.keyring" if FileTest.exists?(".keyring")
+ args.push "--armor"
args.push "--encrypt"
(outtxt, stderrtxt, statustxt, exitstatus) = GnuPG.gpgcall(content, args)
proceed = read_input("Warning: Editor did not exit successfully (exit code #{status.exitstatus}. Proceed?")
exit(0) unless proceed
end
- tempfile.seek(0, IO::SEEK_SET)
- content = tempfile.read
- # zero the file
+ # some editors do not write new content in place, but instead
+ # make a new file and more it in the old file's place.
+ begin
+ reopened = File.open(tempfile.path, "r+")
+ rescue Exception => e
+ STDERR.puts e
+ exit(1)
+ end
+ content = reopened.read
+
+ # zero the file, well, both of them.
newsize = content.length
- tempfile.seek(0, IO::SEEK_SET)
clearsize = (newsize > oldsize) ? newsize : oldsize
- tempfile.print "\0"*clearsize
- tempfile.fsync
+
+ [tempfile, reopened].each do |f|
+ f.seek(0, IO::SEEK_SET)
+ f.print "\0"*clearsize
+ f.fsync
+ end
+ reopened.close
tempfile.close(true)
if content.length == 0
end
end
+class KeyringUpdater
+ def help(parser, code=0, io=STDOUT)
+ io.puts "Usage: #{$program_name} update-keyring [<keyserver>]"
+ io.puts parser.summarize
+ io.puts "Updates the local .keyring file"
+ exit(code)
+ end
+
+ def initialize()
+ ARGV.options do |opts|
+ opts.on_tail("-h", "--help" , "Display this help screen") { help(opts) }
+ opts.parse!
+ end
+ help(ARGV.options, 1, STDERR) if ARGV.length > 1
+ keyserver = ARGV.shift
+ keyserver = 'keys.gnupg.net' unless keyserver
+
+ groupconfig = GroupConfig.new
+ users = groupconfig.get_users()
+ args = %w{--with-colons --no-options --no-default-keyring --keyring=./.keyring}
+
+ system('touch', '.keyring')
+ users.each_pair() do |uid, keyid|
+ cmd = args.clone()
+ cmd << "--keyserver=#{keyserver}"
+ cmd << "--recv-keys"
+ cmd << keyid
+ puts "Fetching key for #{uid}"
+ (outtxt, stderrtxt, statustxt) = GnuPG.gpgcall('', cmd)
+ unless (statustxt =~ /^\[GNUPG:\] IMPORT_OK /)
+ STDERR.puts "Warning: did not find IMPORT_OK token in status output"
+ STDERR.puts "gpg exited with exit code #{ecode})"
+ STDERR.puts "Command was gpg #{cmd.join(' ')}"
+ STDERR.puts "stdout was #{outtxt}"
+ STDERR.puts "stderr was #{stderrtxt}"
+ STDERR.puts "statustxt was #{statustxt}"
+ end
+
+ cmd = args.clone()
+ cmd << '--batch' << '--edit' << keyid << 'minimize' << 'save'
+ (outtxt, stderrtxt, statustxt, ecode) = GnuPG.gpgcall('', cmd)
+ end
+
+
+ end
+end
def help(code=0, io=STDOUT)
io.puts "Usage: #{$program_name} ed"
io.puts " #{$program_name} ls"
+ io.puts " #{$program_name} update-keyring"
io.puts " #{$program_name} help"
io.puts "Call #{$program_name} <command> --help for additional options/parameters"
exit(code)
case ARGV.shift
when 'ls': Ls.new
when 'ed': Ed.new
+ when 'update-keyring': KeyringUpdater.new
when 'help':
case ARGV.length
when 0: help