X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=pws;h=b81da23395cb88ed8de47448ed82dd630542520c;hb=abbf573f63e08c2c259787e251ea13c2d47938a2;hp=cccb42c2214b7ce913319a9bcbd9c6eea15bebe9;hpb=eee618bf1a00ae4db3e3be34e52ca2bff2dbeb25;p=pwstore diff --git a/pws b/pws index cccb42c..b81da23 100755 --- a/pws +++ b/pws @@ -2,7 +2,7 @@ # password store management tool -# Copyright (c) 2008 Peter Palfrader +# Copyright (c) 2008, 2009 Peter Palfrader # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -90,7 +90,12 @@ class GnuPG 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 @@ -102,6 +107,10 @@ class GnuPG 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 @@ -185,7 +194,11 @@ def read_input(query, default_yes=true) while true print "#{query} #{append} " - i = STDIN.readline.chomp.downcase + begin + i = STDIN.readline.chomp.downcase + rescue EOFError + return default_yes + end if i=="" return default_yes elsif i=="y" @@ -219,7 +232,7 @@ class GroupConfig 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| @@ -231,7 +244,12 @@ class GroupConfig 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 @@ -240,6 +258,11 @@ class GroupConfig exit(1) end + if not exitstatus==0 + STDERR.puts "gpg verify failed for .users file" + exit(1) + end + return outtxt end @@ -316,7 +339,7 @@ class GroupConfig 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'] @@ -358,6 +381,10 @@ class GroupConfig end return ok, fprs.uniq end + + def get_users() + return @users + end end class EncryptedFile @@ -402,7 +429,7 @@ 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) @@ -426,6 +453,8 @@ class EncryptedFile 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) @@ -588,15 +617,27 @@ class Ed 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 @@ -624,8 +665,8 @@ class Ed def initialize() ARGV.options do |opts| opts.on_tail("-h", "--help" , "Display this help screen") { help(opts) } - opts.on_tail("-n", "--new" , "Edit new file") { |@new| } - opts.on_tail("-f", "--force" , "Spawn an editor even if the file is probably not readable") { |@force| } + opts.on_tail("-n", "--new" , "Edit new file") { |new| @new=new } + opts.on_tail("-f", "--force" , "Spawn an editor even if the file is probably not readable") { |force| @force=force } opts.parse! end help(ARGV.options, 1, STDERR) if ARGV.length != 1 @@ -657,10 +698,57 @@ class Ed end end +class KeyringUpdater + def help(parser, code=0, io=STDOUT) + io.puts "Usage: #{$program_name} update-keyring []" + 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} --help for additional options/parameters" exit(code) @@ -669,12 +757,13 @@ end def parse_command case ARGV.shift - when 'ls': Ls.new - when 'ed': Ed.new - when 'help': + when 'ls' then Ls.new + when 'ed' then Ed.new + when 'update-keyring' then KeyringUpdater.new + when 'help' then case ARGV.length - when 0: help - when 1: + when 0 then help + when 1 then ARGV.push "--help" parse_command else help(1, STDERR)