#!/usr/bin/perl -w # # $Id: //websites/unixwiz/unixwiz.net/webroot/tools/eudinfo.p#1 $ # # written by: Stephen J. Friedl # Software Consultant # Tustin, California USA # steve@unixwiz.net / www.unixwiz.net # # This program reads through a EUDORA.INI file and reports all the # interesting parameters found within, including the decoded saved # passwords. The file is in WIN.INI format, and we have figured out # how to read the "default" settings plus the per-Persona settings # and report them all. # # Example: # # C> perl eudinfo eudora.ini # --> {default} # Password = he11o43 # MailType = IMAP # RealName = Stephen J. Friedl # POPServer = lnx.unixwiz.net # LoginName = steve # SMTPServer = lnx # ReturnAddr = steve@unixwiz.net # POPAccount = steve@lnx.unixwiz.net # # We also support manual encoding and decoding of obscured passowrds # directly on the command line, which disables the reading of the INI # file from the standard input. # # C> perl eudinfo --encode=hello # Encoding {hello} --> aGVsbG8A # # C> perl eudinfo --decode=aGVsbG8A # Decoding {aGVsbG8A} --> hello # # WIN32 NOTES # ----------- # # The original version of this program read EUDORA.INI from the # standard input, but there is a limitation of Windows NT/2000 # that keeps this from working right when the file is invoked as # a result of filetype assocations: # # C> perl=C:\Perl\bin\Perl.exe -w "%1" %* # C> assoc .p=Perl # # This executes dot-P files (say, eudinfo.p) found in the %PATH% # via the perl interpreter when invoked as "eudinfo", but it # simply doesn't work with redirection of any kind. It does work # when invoked directly as # # C> perl eudinfo.p < eudora.ini # # So we simply have to read filenames on the cmdline. Sorry, Unix. # # BUILDING THIS # ------------- # # We have split "eudinfo.p" and "eudpass.pm" into separate parts # because the decoder is also used in other tools we have. But for # a standalone tool for casual users, it's much easier to just # combine the main driver and the module into one. # # Simply append eudpass.pm to the tail of this module and elide the # "use eudpass;" line near the top. Then it's standalone. # # # COMMAND LINE # ------------ # # --decode=XXXXXXXXXX Provide a manual obscured password and decode it to # the standard error. # # --encode=cleartext Given "cleartext", create the Eudora encoded password # # --version Show program version info and exit # # filename Specify a filename # # HISTORY # ------- # # 1.0 - 2002/02/23 - initial development # # 1.1 - 2002/02/27 - now read eudora.ini only from the cmdline use strict; # use eudpass; my $version = "1.1 - 2002/02/27"; my $versinfo = "eudinfo v$version - steve\@unixwiz.net"; my $manual = 0; # count of manual passwords my @FILES = (); foreach ( @ARGV ) { if ( m/^--help/ ) { print STDERR < $decpass\n"; ++$manual; } elsif ( m/^--encode=(.+)/ ) # --encode=PASSWORD { my $decpass = $1; my $encpass = encode_Eudora_password($decpass); print "Encoding {$decpass} --> $encpass\n"; ++$manual; } elsif ( m/^-/ ) { die "ERROR: {$_} is invalid cmdline param (try --help)\n"; } elsif ( -r $_ ) { push @FILES, $_; } else { die "ERROR: cannot read file {$_}\n"; } } # if we had any manual passwords, we're done exit 0 if $manual; # ------------------------------------------------------------------------ # Now run through each file found: it's an error to have no files. # die "ERROR: no files to process! (try --help)\n" if @FILES == 0; foreach my $fname ( @FILES ) { print "Processing $fname\n"; open(F, $fname) or die "ERROR: cannot open {$fname}\n"; my @LINES = ; # read the file close F; my %PERSONAS = (); # no personas read parse_Eudora_INI(\%PERSONAS, @LINES); # decode the file! foreach my $pname ( sort keys %PERSONAS ) { print " --> {$pname}\n"; my $ref = $PERSONAS{$pname}; foreach my $name ( keys %{ $ref } ) { my $value = $ref->{$name}; # decode the password if ( $name eq 'Password' and length $value > 0 ) { $value = decode_Eudora_password($value); } printf " %-12s = %s\n", $name, $value; } } } # # parse_Eudora_INI # # Given a ref to a %PERSONAS hash, plus a list of lines that came # from a Eudora.INI file, parse it to get the interesting values # and fill the hash with each section's settings. # sub parse_Eudora_INI { my $href = shift; # ref to the %PERSONAS hash my @LINES = @_; # list of EUDORA.INI lines my $pref = undef; # ref to current item anon hash foreach ( @LINES ) { # dump trailing whitespace, ignore blank & comment lines s/\s+$//; next if length == 0 or m/^#/; # -------------------------------------------------------- # This is the tricky part: We are looking for the WIN.INI- # style section of either # # [Settings] # or # # [Persona-my name here] # # These are the only two sections we're interested in, and # they both have the same format. We should only have one # "Settings" section, though multipler personas are probably # possible. # # We treat [Settings] as the name "default", and the rest of # the personas are named accordingly. # if ( m/^\[ ( Settings # "default" | Persona-[^]]+ # named persona ) \]/ix ) { # ------------------------------------------------ # The persona name is in $1, so strip off the # "Persona-" part if found, else set to "default". # my $pname = "$1"; if ( not ( $pname =~ s/^Persona-// ) ) { $pname = "default"; } $pref = $href->{$pname} = { POPAccount => "", RealName => "", SMTPServer => "", LoginName => "", Password => "", ReturnAddr => "", MailType => "", }; next; } # -------------------------------------------------------- # This tests for a setting that's not a persona: this # always turns off the current persona reference. # elsif ( m/^\[/ ) { $pref = undef; next; } next if not defined $pref; # -------------------------------------------------------- # Now we're to look for the individual settings. Split the # line into the NAME=VALUE pair, then store the result into # the hash for each one that matches. my($name, $value) = split( m/\s*=\s*/, $_, 2); if ( $value eq "1" ) { $pref->{MailType} = 'POP' if $name =~ m/^UsesPOP$/i; $pref->{MailType} = 'IMAP' if $name =~ m/^UsesIMAP$/i; } $pref->{POPAccount} = $value if $name =~ m/^POPaccount$/i; $pref->{RealName } = $value if $name =~ m/^Realname$/i; $pref->{SMTPServer} = $value if $name =~ m/^SMTPserver$/i; $pref->{POPServer } = $value if $name =~ m/^PoPServer$/i; $pref->{ReturnAddr} = $value if $name =~ m/^ReturnAddress$/i; $pref->{Password } = $value if $name =~ m/^SavePasswordText$/i; $pref->{LoginName } = $value if $name =~ m/^LoginName$/i; } } #!/usr/bin/perl -w # # $Id: //websites/unixwiz/unixwiz.net/webroot/tools/eudinfo.p#1 $ # # written by : Stephen J. Friedl # Software Consultant # Tustin, California USA # steve@unixwiz.net / www.unixwiz.net # # This module performs decoding of Eudora passwords as found in a # EUDORA.INI file on Windows systems. In this file, the [SETTINGS] # section can contain a token "SavePasswordText=@@@@@@@" value, This # string contains an obscured password that is presented to the # POP/IMAP server for picking up email. # # Eudora obscures passwords by turning three characters into four # output bytes, doing so six bits at a time, so the obscured passwd # is always an even multiple of four characters. It's an error if # this doesn't turn out to be the case. # # Tested on 5.1 and reported to be correct for older versions. # # EUDORA.INI FILE SUMMARY # ----------------------- # # The EUDORA.INI file is normally found in # # C:\Program Files\Qualcomm\Eudora\EUDORA.INI # # but is officially described in the registry value: # # HKEY_CURRENT_USER\ # Software\ # Qualcomm\ # Eudora\ # CommandLine\ # current # # This value contains three words separated by spaces (it doens't # allow spaces in filenames): # # - name of executable # - working directory for mail files # - path of EUDORA.INI # # A stripped down version of the EUDORA.INI file shows a WIN.INI style # format. # # [SETTINGS] # POPAccount=steve@unixwiz.net # RealName=Steve Friedl # SMTPServer=linux.unixwiz.net # PopServer=linux.unixwiz.net # ReturnAddress=steve@unixwiz.net # LoginName=steve # SavePasswordText=eW91J3JlIGtpZGRpbmcA # # The "SavePasswordText" contains the obscured password, which is # decoded by this routine. There is also a section of "Personas", # which can describe other users as well, but the format should # be more or less obvious. # use strict; # ------------------------------------------------------------------------ # This "BEGIN" is useful for when we wish to concatenate this module to # the end of a main program ("eudinfo"?) to avoid the need to deal with # perl library paths: simply stick this at the end of the main program, # elide the "use eudpass;", and it works. Without the BEGIN block, we don't # get our "private" $alphabet variable initialized. This shouldn't hurt # anything when used as a module. # my $alphabet; BEGIN { $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; } sub encode_Eudora_password { my $decpass = shift; my @PCHARS = split( m//, $decpass ); my $encpass = ""; while ( @PCHARS ) { # -------------------------------------------------------- # STEP 1 # # Fill up 24 bits of user data eight bits at a time, and # they fill from the MSB first side. Bytes that don't exist # are left at zero, but we don't count them as "in the # buffer" either. # my $bitbuf = 0; # 24-bit buffer of the input bytes my $nbits = 0; # how many of those bits are value? foreach my $shiftval ( 16, 8, 0 ) { if ( @PCHARS ) { $nbits += 8; $bitbuf |= ord(shift @PCHARS) << $shiftval; } } # -------------------------------------------------------- # STEP 2 # # Take the three 8-bit bytes and split them up into four # six-bit tokens, and for each one, index into the alphabet # to get the visible ASCII token. But we only do this # indexing if the six-bit token has at least one bit that # came from a user password bit. Otherwise we use '=' for # padding. # # Example: if the input is two bytes, that leaves output # token #4 devoid of any input bytes: #4 will be "=" padding. # # =======1======= =======2======= =======3======= INPUTS (3) # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|.|.|.|.|.|.|.|.| bit buffer # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # =====1===== =====2===== =====3===== =====4===== OUTPUTS (4) # foreach my $shiftval ( 18, 12, 6, 0 ) { if ( $nbits <= 0 ) { $encpass .= '='; # padding } else { my $tok = ($bitbuf >> $shiftval) & 0x3F; $encpass .= substr( $alphabet, $tok, 1); $nbits -= 6; } } } return $encpass; } sub decode_Eudora_password { my $encpass = shift; # encoded input password # ---------------------------------------------------------------- # Padding bytes (=) are only allowed at the *end* of the string. # if ( $encpass =~ m/=[^=]/ ) { # ERROR: padding bytes can only be trailing return undef; } my @PCHARS = split( m//, $encpass); # individual password chars my $decpass = ""; # resulting decoded password while ( @PCHARS >= 4 ) { my $bitbuf = 0; # buffer for each my $nbits = 0; foreach my $shiftval ( 18, 12, 6, 0 ) { # ------------------------------------------------ # padding bytes are always ignored # next if ( my $c = shift @PCHARS) eq '='; # ------------------------------------------------ # turn alphabet char -> six-bit token and shift it # into the proper place in the buffer. # if ( ($c = index($alphabet, $c)) < 0 ) { # ERROR: bogus base64 token return undef; } $bitbuf += $c << $shiftval; $nbits += 6; } foreach my $shiftval ( 16, 8, 0 ) { last if $nbits <= 0; # no more bytes? $decpass .= chr( ($bitbuf >> $shiftval) & 0xFF ); $nbits -= 8; } } # ---------------------------------------------------------------- # If we have *any* encoded password bytes left, it means we did # not have an even multiple of four. This is an error: failure. # if ( @PCHARS ) { # ERROR: encoded password not multiple of four chars return undef; } return $decpass; } 1;