[Dovecot] [checkpassword] I can't get a password from fd3
Hi all,
I'm trying to implement checkpassword authentication with a simple bash script. In some way I can't get the password from file descriptor 3.
The start of script looks like this:
#!/bin/bash
read -d '\0' -r -u 3
USERNAME="${REPLY}"
read -d '\0' -r -u 3
PASSWORD="${REPLY}"
if [ -z "${USERNAME}" ] || [ -z "${PASSWORD}" ] then exit 111 fi
It always exits with 111, because PASSWORD is empty. In whatever way I try to retrieve data from fd3, I never happen to get more data than the username.
Is this a known problem? Am I doing something silly? Replies are much appreciated!
Durk
FYI: GSSAPI auth works perfect and PAM worked before switching to checkpassword too.
OS version : Debian 5.0 lenny amd64 Dovecot version : 1.0.15 (Debian version 1.0.15-2.3)
Parts of /etc/dovecot/dovecot.conf:
protocols: imap ssl_cert_file: /etc/ssl/certs/cert.pem ssl_key_file: /etc/ssl/private/cert.key ssl_cipher_list: ALL:!LOW:!SSLv2 login_dir: /var/run/dovecot/login login_executable: /usr/lib/dovecot/imap-login first_valid_uid: 998 last_valid_uid: 998 first_valid_gid: 998 last_valid_gid: 998 mail_privileged_group: mail mail_location: maildir:/srv/vmail/%Ld/%n lock_method: flock maildir_copy_with_hardlinks: yes auth default: mechanisms: gssapi plain krb5_keytab: /etc/dovecot/krb5.keytab verbose: yes debug: yes passdb: driver: checkpassword args: /usr/bin/dovecot-checkpassword userdb: driver: ldap args: /etc/dovecot/dovecot-ldap.conf
On Mon, 2009-01-12 at 18:31 +0100, Durk Strooisma wrote:
#!/bin/bash
read -d '\0' -r -u 3
Are you sure this is supposed to work? \0 character is an end-of-string character in C language, and I wouldn't be surprised if read simply didn't support it as delimiter.
Well if I try to read everything, without delimiting using \0, I don't get more data...
Durk
On Mon, 2009-01-12 at 20:54 +0100, Durk Strooisma wrote:
On Mon, 2009-01-12 at 18:31 +0100, Durk Strooisma wrote:
#!/bin/bash
read -d '\0' -r -u 3
Are you sure this is supposed to work? \0 character is an end-of-string character in C language, and I wouldn't be surprised if read simply didn't support it as delimiter.
Well if I try to read everything, without delimiting using \0, I don't get more data...
Again the same reason: maybe read just doesn't support it. I actually tried and couldn't really get it to work with zsh. With bash it worked even worse.
Anyway I'm sure Dovecot works right, and the problem is just that you can't do with shell scripting what you're trying to (at least not this way).
On Mon, 2009-01-12 at 20:54 +0100, Durk Strooisma wrote:
On Mon, 2009-01-12 at 18:31 +0100, Durk Strooisma wrote:
#!/bin/bash
read -d '\0' -r -u 3
Are you sure this is supposed to work? \0 character is an end-of-string character in C language, and I wouldn't be surprised if read simply didn't support it as delimiter.
Well if I try to read everything, without delimiting using \0, I don't get more data...
Again the same reason: maybe read just doesn't support it. I actually tried and couldn't really get it to work with zsh. With bash it worked even worse.
Anyway I'm sure Dovecot works right, and the problem is just that you can't do with shell scripting what you're trying to (at least not this way).
Okay, thanks for verifying. What kind of scripting language would you suggest for checkpassword instead? Or should I just move on to C?
Durk
Durk Strooisma wrote:
Okay, thanks for verifying. What kind of scripting language would you suggest for checkpassword instead?
Perl works for me. Extract from such script:
use constant CHECKPASSWORD_MAX_LEN => 512;
[...]
my $input = IO::Handle->new_from_fd(3, "r");
if (not defined $input) {
internal_error('read_from_dovecot - getting file descriptor failed');
return;
}
my $length = undef;
my $buffer = '';
do {
$length = $input->read($buffer, CHECKPASSWORD_MAX_LEN - length($buffer), length($buffer)) ;
if ($input->error or not defined $length) {
internal_error('read_from_dovecot - read failed');
$input->close;
return;
}
} while ($length != 0);
$input->close;
my ($user, $password, $rest) = split /\0/, $buffer, 3;
[...]
It is also possible to skip the execution of "checkpassword-reply". Assuming that dovecot will never change the protocol it expects from "checkpassword-reply", it is possible to write the reply to filedescriptor 4 in your script directly and skip invocation of the intermediate program. Works great.
Extract from such script:
# Exit Codes
use constant AUTH_OK => 0;
use constant AUTH_FAIL => 1;
use constant AUTH_ERROR => 111;
[...]
my $output = IO::Handle->new_from_fd(4, "w");
if (not defined $output) {
internal_error('write_to_dovecot - getting filedescriptor failed');
return;
}
$output->autoflush(1);
# Returning Username and Homedir to dovecot
my $response = "user=${user}\tuserdb_home=${homedir}\t";
$output->print($response);
if ($output->error) {
internal_error('write_to_dovecot - write failed');
}
$output->close;
[...]
Exit your script with appropriate Exit Code (see constant definition above).
As dovecot does not provide RADIUS as authentication mechanism, I'm using CheckPassword interface with a perl script to ask a list of RADIUS servers and return everything which is necessary back to dovecot (-> userdb prefetch).
Heiko
Heiko Schlichting Freie Universität Berlin heiko@CIS.FU-Berlin.DE Zentraleinrichtung für Datenverarbeitung (ZEDAT) Telefon +49 30 838-54327 Fabeckstraße 32 Telefax +49 30 838454327 14195 Berlin
Thanks for the example and your info about fd4.
Durk
Durk Strooisma wrote:
Okay, thanks for verifying. What kind of scripting language would you suggest for checkpassword instead?
Perl works for me. Extract from such script:
use constant CHECKPASSWORD_MAX_LEN => 512; [...] my $input = IO::Handle->new_from_fd(3, "r"); if (not defined $input) { internal_error('read_from_dovecot - getting file descriptor failed'); return; } my $length = undef; my $buffer = ''; do { $length = $input->read($buffer, CHECKPASSWORD_MAX_LEN - length($buffer), length($buffer)) ; if ($input->error or not defined $length) { internal_error('read_from_dovecot - read failed'); $input->close; return; } } while ($length != 0); $input->close; my ($user, $password, $rest) = split /\0/, $buffer, 3; [...]
It is also possible to skip the execution of "checkpassword-reply". Assuming that dovecot will never change the protocol it expects from "checkpassword-reply", it is possible to write the reply to filedescriptor 4 in your script directly and skip invocation of the intermediate program. Works great.
Extract from such script:
# Exit Codes use constant AUTH_OK => 0; use constant AUTH_FAIL => 1; use constant AUTH_ERROR => 111; [...] my $output = IO::Handle->new_from_fd(4, "w"); if (not defined $output) { internal_error('write_to_dovecot - getting filedescriptor failed'); return; } $output->autoflush(1);
# Returning Username and Homedir to dovecot my $response = "user=${user}\tuserdb_home=${homedir}\t";
$output->print($response); if ($output->error) { internal_error('write_to_dovecot - write failed'); } $output->close; [...]
Exit your script with appropriate Exit Code (see constant definition above).
As dovecot does not provide RADIUS as authentication mechanism, I'm using CheckPassword interface with a perl script to ask a list of RADIUS servers and return everything which is necessary back to dovecot (-> userdb prefetch).
Heiko
Heiko Schlichting Freie Universität Berlin heiko@CIS.FU-Berlin.DE Zentraleinrichtung für Datenverarbeitung (ZEDAT) Telefon +49 30 838-54327 Fabeckstraße 32 Telefax +49 30 838454327 14195 Berlin
Hello Durk,
Am Montag, 12. Januar 2009 schrieb Durk Strooisma:
I'm trying to implement checkpassword authentication with a simple bash script. In some way I can't get the password from file descriptor 3.
The start of script looks like this:
#!/bin/bash
read -d '\0' -r -u 3 You are missing the correct syntax to interpret backslash escapes here:
read -d $'\0' -r -u 3
will work.
Lutz
read -d '\0' -r -u 3 You are missing the correct syntax to interpret backslash escapes here:
read -d $'\0' -r -u 3
will work.
Thanks a lot!! I was indeed missing the $ sign! And it is nicely documented here:
http://www.gnu.org/software/bash/manual/bashref.html#ANSI_002dC-Quoting
I never doubted the syntax, because when I didn't provide a delimiter (in that case read uses a newline), still nothing but the username could be read, but there is no newline after the username... That made me think there was something else wrong. So it seems that read always stops reading at a nul character (and removing any other data) if it cannot find the provided or default delimiter.
Durk
participants (4)
-
Durk Strooisma
-
Heiko Schlichting
-
Lutz Preßler
-
Timo Sirainen