#! /usr/bin/perl # # Copyright (c) 2008 William K. Cole. All rights reserved. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice, this permission notice, and the following disclaimer # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # crampass.pl: a simplistic script that replicates one function of the # dovecotpw tool that is included in the Dovecot mailstore # server, turning a password given on the command line into # a string that Dovecot can use as a 'CRAM-MD5' auth string. # This string consists of the two 16-byte state arrays that # are sometimes referred to as "contexts" and are generated # by feeding 2 differently padded versions of the password # into MD5 and recording the resulting (unfinalized) state # arrays. This is an intermediate step in the HMAC-MD5 # algorithm, and the resulting contexts can be used to # initialize MD5 hash objects for authentication of any # message. The contexts cannot be programmatically converted # back to the original password, and so are marginally safer # to store on a server than a plaintext password, which is # the only other option for use with the CRAM-MD5 auth # mechanism. This script was written in response to a query # on the Dovecot mailing list by Douglas Willcocks on # 2008-04-11 and ensuing discussion. It is intended as a # demo, not a production tool. It assumes that the immediate # user is neither malicious nor a fool. # # References: RFC1321(MD5) RFC2195(CRAM-MD5) RFC2104(HMAC) # ID:draft-ietf-sasl-crammd5-09(CRAM-MD5 as SASL mech) # uncomment to make verbose and run dovecotpw for comparison (must be in $PATH) #$debug=1; # Note dependency: use Digest::Perl::MD5; @ARGV == 1 or die "usage: crampass.pl \n"; $pass = shift @ARGV; defined $debug and print "using /$pass/ as the user password\n"; $secret=$pass; if (length $secret > 64) { defined $debug and print "hashing down long password\n"; $secret = Digest::Perl::MD5::md5($secret); } defined $debug and print "using /$secret/ as the shared secret\n"; $Ki = "$secret" ^ (chr(0x36) x 64); $Ko = "$secret" ^ (chr(0x5c) x 64); defined $debug and print "HMAC keys: inner=/$Ki/ outer=/$Ko/\n"; $innermd5 = Digest::Perl::MD5->new; $innermd5->add($Ki); $Ci = pack 'V4', @{$innermd5->{_state}}; $outermd5 = Digest::Perl::MD5->new; $outermd5->add($Ko); $Co = pack 'V4', @{$outermd5->{_state}}; $innerhex=Digest::Perl::MD5::_encode_hex($Ci); $outerhex=Digest::Perl::MD5::_encode_hex($Co); defined $debug and print "hex: inner=/$innerhex/ outer=/$outerhex/\n"; print "{CRAM-MD5}$outerhex$innerhex\n"; defined $debug and print "dovecotpw says:\n"; defined $debug and system("dovecotpw -s CRAM-MD5 -p \"$pass\"");