#!@RUBY@ # # dtcpauth, manipulate POP authorization DB # # # Copyright (c) 2002-2004 Hajimu UMEMOTO # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Mahoroba: src/dtcp/dtcpauth.rb,v 1.4 2005/12/28 08:33:06 ume Exp $ # require 'getopts' require 'dbm' require 'etc' POPAUTHUID = 'pop' POPAUTHDB = '@POPAUTHDB@' #$debug = true def usage $stderr.print "usage: #{File.basename($0)} [--init] [--list [user | ALL]] [--delete user] [--user user [password]]\n" end def errmsg(msg) $stderr.print "#{File.basename($0)}: #{msg}\n" end class PopAuthDb def each @f.each { |user, password| yield(user.chop, obscure(strip_null(password))) } end def user(user) key = user + "\0" if !@f.has_key?(key) errmsg("no entry for \"#{user}\" in POP authentication DB") return false end return obscure(strip_null(@f[key])) end def set(user, password) @f[user + "\0"] = obscure(password) + "\0\0" end def delete(user) key = user + "\0" if !@f.has_key?(key) errmsg("User '#{user}' not found in authentication database") return false end @f.delete(key) return true end def close @f.close @f = nil Process.euid = @origuid end def opend? if @f return true end return false end private def initialize(mode = nil) pw = Etc.getpwnam(POPAUTHUID) if !pw errmsg("no user named #{POPAUTHUID}") return end @origuid = Process.euid begin Process.euid = pw[2] rescue errmsg("no permission to asscess POP authentication DB") return end @f = DBM.open(POPAUTHDB, mode) if !@f if mode errmsg("no password database found") end Process.euid = @origuid return end end def strip_null(p) while p.length > 0 && p[p.length - 1] == 0 p = p[0, p.length - 1] end return p end def obscure(p) for i in 0 .. p.length - 1 p[i] = [p[i] ^ 0xff].pack('C') end return p end end def getpassword(prompt = "Password") password = '' while true open('/dev/tty', 'r') { |tty| system("stty -echo") $stderr.print "#{prompt}: " password = tty.readline.chop! } system("stty sane") $stderr.print "\n" if password.size > 4 break end print "Please use a longer password.\n" end return password end def initdb popauthdb = PopAuthDb.new if popauthdb.opend? popauthdb.close errmsg("there is POP authentication DB already") return false end popauthdb = PopAuthDb.new(0600) if popauthdb.opend? return false end popauthdb.close return true end def listuser(popauthdb, user) if user == 'ALL' popauthdb.each { |u, p| if $debug print "#{u.ljust(16)}: #{p}\n" else print "#{u.ljust(16)}: APOP\n" end } else p = popauthdb.user(user) if p if $debug print "#{user.ljust(16)}: #{p}\n" else print "#{user.ljust(16)}: APOP\n" end end end return true end def setuser(popauthdb, user, password) if !password print "Changing only APOP password for #{user}.\n" password = getpassword("New Password") password2 = getpassword("Retype new Password") if password != password2 print "Mismatch -- password unchanged.\n" return false end end popauthdb.set(user, password) return true end if !getopts('dilu', 'delete', 'init', 'list', 'user') usage() exit 1 end $OPT_delete = true if $OPT_d $OPT_init = true if $OPT_i $OPT_list = true if $OPT_l $OPT_user = true if $OPT_u optnum = 0 [$OPT_delete, $OPT_init, $OPT_list, $OPT_user].each { |opt| if opt optnum += 1 end } if optnum != 1 usage() exit 1 end if $OPT_init r = initdb() else if ARGV.length < 1 usage() exit 1 end popauthdb = PopAuthDb.new if !popauthdb.opend? exit 1 end if $OPT_list r = listuser(popauthdb, ARGV[0]) elsif $OPT_user r = setuser(popauthdb, ARGV[0], ARGV[1]) elsif $OPT_delete r = popauthdb.delete(ARGV[0]) end popauthdb.close end if !r exit 1 end exit 0