# -*-Perl-*- ################################################################ ### ### GetPass.pm ### ### Author: Internet Message Group ### Created: Apr 30, 1997 ### Revised: Jul 4, 2004 ### my $PM_VERSION = "IM::GetPass.pm version 20031028(IM146)"; package IM::GetPass; require 5.003; require Exporter; use IM::Config; use IM::Util; use integer; use strict; use vars qw(@ISA @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(getpass getpass_interact loadpass savepass connect_agent talk_agent findpass); sub getpass($$$$) { my($proto, $auth, $host, $user) = @_; my $pass = ''; my $agtfound = 0; my $interact = 0; if (&usepwagent()) { $pass = &loadpass($proto, $auth, $host, $user); $agtfound = 1 if ($pass ne ''); } if ($pass eq '' && &usepwfiles()) { $pass = &findpass($proto, $auth, $host, $user); } my $prompt = lc("$proto/$auth:$user\@$host"); if ($pass eq '') { $pass = &getpass_interact("Password ($prompt): "); $interact = 1; } return ($pass, $agtfound, $interact); } sub getpass_interact($) { my($prompt) = @_; my($secret, $termios, $c_lflag); if (! -t STDIN) { # stty is not effective for Mule since it's not terminal base. # Anyway, Mew never echos back even if getpass echos back. } elsif (eval 'require POSIX' & !win95p()) { import POSIX qw(termios_h); $termios = new POSIX::Termios; $termios->getattr(fileno(STDIN)); $c_lflag = $termios->getlflag; $termios->setlflag($c_lflag & ~&POSIX::ECHO); $termios->setattr(fileno(STDIN), &POSIX::TCSANOW); } elsif (unixp()) { # non-POSIX-ish UNIX. # stty might be available. my($OldPath) = $ENV{'PATH'}; # for SUID version $ENV{'PATH'} = '/bin:/usr/bin'; system('/bin/stty -echo'); # Ignore errors. $ENV{'PATH'} = $OldPath; } # POSIX doesn't exist for Win95, sigh. print STDERR $prompt; flush('STDERR'); chomp($secret = ); print STDERR "\n"; flush('STDERR'); if (! -t STDIN) { # no operation } elsif (defined $termios) { # POSIX-ish $termios->setlflag($c_lflag); $termios->setattr(fileno(STDIN), &POSIX::TCSANOW); } elsif (unixp()) { # non-POSIX-ish UNIX. my($OldPath) = $ENV{'PATH'}; # for SUID version $ENV{'PATH'} = '/bin:/usr/bin'; system('/bin/stty echo'); # Ignore errors. $ENV{'PATH'} = $OldPath; } return $secret; } sub loadpass($$$$) { my($proto, $auth, $path, $user) = @_; local($_); my $key = &connect_agent(0); return '' if ($key eq ''); my @keys = unpack('C*', $key); my $pass = &talk_agent("LOAD\t$proto\t$auth\t$path\t$user\n"); if ($pass =~ /^PASS\t(.*)/) { my @tmp1 = unpack('C*', pack('H*', $1)); my $sum1 = $keys[0]; foreach (@tmp1) { $sum1 += $keys[1]; my $tmp2 = $_; $_ -= $sum1; $_ &= 0xff; $sum1 = $tmp2; } return pack('C*', @tmp1); } else { return ''; } } sub savepass($$$$$) { my($proto, $auth, $path, $user, $pass) = @_; local($_); my $key = &connect_agent(0); return '' if ($key eq ''); my @keys = unpack('C*', $key); my @tmp1 = unpack('C*', $pass); my $sum1 = $keys[0]; foreach (@tmp1) { $sum1 += $_ + $keys[1]; $sum1 &= 0xff; $_ = $sum1; } $pass = unpack('H*', pack('C*', @tmp1)); &talk_agent("SAVE\t$proto\t$auth\t$path\t$user\nPASS\t$pass\n", 0); } sub connect_agent($) { my($surpresserror) = shift; require Socket && import Socket; my $realuser = im_getlogin(); unless ($realuser) { im_warn("pwagent: can not get login name\n") unless ($surpresserror); return ''; } my $dir = &pwagent_tmp_path() . "-$realuser"; my $port = &pwagentport(); if ($port > 0) { unless (socket(SOCK, &AF_INET, &SOCK_STREAM, 0)) { im_warn("pwagent: socket: $!\n") unless ($surpresserror); return ''; } my $sin = sockaddr_in($port, inet_aton('127.0.0.1')); unless (connect(SOCK, $sin)) { im_warn("pwagent: connect: $!\n") unless ($surpresserror); return ''; } } else { my $name = "$dir/pw"; unless (-S $name) { im_warn("pwagent: can not access to socket: $name\n") unless ($surpresserror); return ''; } my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev) = stat($dir); if ($mode & 0077) { im_warn("pwagent: invalid mode: $dir\n") unless ($surpresserror); return ''; } ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev) = stat($name); if ($mode & 0077) { im_warn("pwagent: invalid mode: $name\n") unless ($surpresserror); return ''; } unless (socket(SOCK, &AF_UNIX, &SOCK_STREAM, 0)) { im_warn("pwagent: socket: $!\n") unless ($surpresserror); return ''; } my $sun = sockaddr_un($name); unless (connect(SOCK, $sun)) { im_warn("pwagent: connect: $!\n") unless ($surpresserror); return ''; } } select(SOCK); $| = 1; select(STDOUT); my $res = ; chomp($res); return $res; } sub talk_agent($) { my($msg) = shift; print SOCK $msg; my $res = ; shutdown (SOCK, 2); close(SOCK); chomp($res); return $res; } sub findpass($$$$) { my($proto, $auth, $host, $user) = @_; local($_); my($passfile); foreach $passfile (split(',', &pwfiles())) { $passfile = &expand_path($passfile); next unless (open (PASSFILE, "<$passfile")); while () { chomp; next if (/^(#.*)?$/); # s/\s+(\#.*)?$//; # remove comments if (/^(\S+)\s+(\S+)\s+(\S+)\s+(\S.+)$/) { my($tmp_host, $tmp_user, $tmp_pass) = ($2, $3, $4); my($tmp_proto, $tmp_auth) = split('/', $1); if (($tmp_proto eq $proto) && ($tmp_auth eq $auth) && ($tmp_host eq $host) && ($tmp_user eq $user)) { close (PASSFILE); return $tmp_pass; } } } close (PASSFILE); } return ''; } 1; __END__ =head1 NAME IM::GetPass - get password from tty or ... =head1 SYNOPSIS use IM::GetPass; ($pass, $agtfound, $interact) = getpass('imap', $auth, $host, $user); =head1 DESCRIPTION The I module handles password for mail/news servers. This modules is provided by IM (Internet Message). =head1 COPYRIGHT IM (Internet Message) is copyrighted by IM developing team. You can redistribute it and/or modify it under the modified BSD license. See the copyright file for more details. =cut ### Copyright (C) 1997, 1998, 1999 IM developing team ### 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. ### 3. Neither the name of the team nor the names of its contributors ### may be used to endorse or promote products derived from this software ### without specific prior written permission. ### ### THIS SOFTWARE IS PROVIDED BY THE TEAM 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 TEAM 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.