It's a month ago but maybe it helps anyway.
i just store passwords in text files. i am looking for a secure way =20=
of
keeping these passwords and using it thro ruby as my progs need the
pass.
for eg. i send auto IM using bot and need my IM pass to be kept
secure.
I don't know for sure if the following can help:
http://pog.rubyforge.org/doc/
Otherwise I had a similar problem in a daemon.
I solved it like that:
M::main_loop() # entry point
M:
asswd_eventloop() # daemon mode, use an UNIX domain socket
M::set_password_interactive() # interactive mode (CLI without socket)
M:
asswordStorage saves the username password tuple in the memory by
encrypting the tuple. It has all methods you need to get and set =20
passwords.
It uses OpenSSL::Cipher::Cipher.new("AES-256-CBC") and a generated =20
random
key with key length 52 to save the tuple in memory.
If you just need an interactive way to set your passsword then just =20
use the
*_interactive() methods from M, without the client and without =20
M:
asswd_eventloop().
Disclaimer:
I am not a crypto expert and maybe there are a few timing issues in the
following code!
#server:
require 'rubygems'
require 'termios'
require 'openssl'
module M
class ShutdownException < StandardError; end
@@pw_storage =3D nil # safe password storage if anyone can read your =20=
memory
# If you change the next line, don't forget to change it in =20
client.rb, too!
PASS_UNIX_SOCK=3D"/tmp/my.sock"
class PasswordStorage
def initialize
@secret =3D {}
@shared_key =3D {}
end
# PasswordStorage::get_password returns the cleartext password =20
for a user.
def get_password(username=3Dnil)
decode(@secret[username], username) if username
end
def set_password_interactive(username=3D"")
# get password from prompt at the start
print "provide password for #{username}: "
begin
M::echo(false)
passwd =3D STDIN.gets.strip
ensure
M::echo(true)
end
if passwd.empty?
STDERR.puts("An empty password is not allowed.")
exit
end
@secret[username] =3D encode(passwd,username)
passwd=3D""
puts ""
end
def set_password(user=3Dnil, passwd=3Dnil)
if user.nil? or passwd.nil? or user.empty? or passwd.empty?
return false
else
@secret[user] =3D encode(passwd,user)
end
end
# private Methods of PasswordStorage
private
def encode(s,username)
cipher =3D OpenSSL::Cipher::Cipher.new("AES-256-CBC")
@iv =3D @iv || cipher.random_iv
cipher.iv =3D @iv
# for each user we want to have a long random key to encrypt the
# user/passwd storage
@shared_key[username] =3D @shared_key[username] || =20
OpenSSL::Random::random_bytes(52)
cipher.encrypt
@shared_key[username] << "\0" until =20
@shared_key[username].length =3D=3D 52
cipher.key =3D @shared_key[username]
return (cipher.update(s) << cipher.final)
end
def decode(key,username)
cipher =3D OpenSSL::Cipher::Cipher.new("AES-256-CBC")
cipher.iv =3D @iv
cipher.decrypt
cipher.key =3D @shared_key[username]
return (cipher.update( key ) << cipher.final)
end
end # PasswordStorage
module_function
# getter/setter
def getpass(username=3Dnil)
@@pw_storage.get_password(username)
end
def setpass_interactive(username=3Dnil)
@@pw_storage =3D @@pw_storage || M:
asswordStorage.new
@@pw_storage.set_password_interactive(username)
end
def setpass(username=3Dnil, password=3Dnil)
@@pw_storage =3D @@pw_storage || M:
asswordStorage.new
@@pw_storage.set_password(username,password) unless username.nil? =20=
or password.nil?
end
def clean_unixsocket
File.delete(PASS_UNIX_SOCK) if File.exists? PASS_UNIX_SOCK
end
# It reads the passwd from a unix domain socket.
def passwd_eventloop
clean_unixsocket
res =3D ""
server =3D UNIXServer.new(PASS_UNIX_SOCK)
begin
sock =3D server.accept_nonblock
res =3D sock.readpartial(4096).split(" ")
sock.close
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, =20
Errno::EINTR, EOFError
IO.select([server])
retry
end
clean_unixsocket
# res: [user passwd]
if @@pw_storage.nil?
b =3D []
b << setpass(res.shift,res.shift)
return b.all?
end
return false
end
# M::main_loop
# The daemon mode has two states, user/password set and not set.
def main_loop
begin
Signal.trap("INT") {raise ShutdownException }
Signal.trap("TERM") {raise ShutdownException }
# Comment out, if you want to use interactive mode
passwd_eventloop
# Uncomment, if you want to use it without reading from socket.
#set_password_interactive
loop do
#do your stuff...
end # loop
rescue ShutdownException
clean_unixsocket
end
end
end # module M
# interactive mode
#M::setpass_interactive("user")
#puts M::getpass("user")[
# daemon mode
M::main_loop()
# end server
# client
#!/usr/bin/env ruby -wKU
require 'socket'
require 'rubygems'
require 'termios'
PASS_UNIX_SOCK=3D"/tmp/my.sock"
unless File.exists? PASS_UNIX_SOCK
puts "server is not running"
exit
end
# enable/disable echo on prompt
def echo(on=3Dtrue, masked=3Dfalse)
term =3D Termios::getattr( $stdin )
if on
term.c_lflag |=3D ( Termios::ECHO | Termios::ICANON )
else # off
term.c_lflag &=3D ~Termios::ECHO
term.c_lflag &=3D ~Termios::ICANON if masked
end
Termios::setattr( $stdin, Termios::TCSANOW, term )
end
print "user:"
user =3D Kernel.gets.strip
puts ""
print "#{user}'s password: "
echo(false)
passwd =3D Kernel.gets.chomp
UNIXSocket.open PASS_UNIX_SOCK do |socket|
socket.write_nonblock("#{user} #{passwd}")
end
echo(true)
puts ""
puts "Thank you for providing the necessary information"
# end client
regards, Sandor Sz=FCcs
--=