I just wanted to share a use for authpipe that others might find useful.

I wanted to use a virtual shared folder setup in Courier-IMAP so that I could use IMAP folder ACLs. However, I needed to do username/password checks against a Windows Active Directory controller. I wanted to set up Courier-IMAP in such a way that I didn't touch the LDAP schema of the AD controller, so I decided to use userdb to store the account information. However, because I wanted to check usernames/passwords against the AD controller, I couldn't store the passwords in the userdb files.

I decided that rather than write my own module, I would stack a "filter" module of sorts on top of the others. This is possible only because of the way that authdaemond uses the modules -- if for any request a module returns "FAIL", then authdaemond makes the same request from the next module in its list. I wrote a shell script, suitable for use with the authpipe module, and a tiny C program that checks username/password pairs via PAM (and I used pam_winbind, though you could use any PAM module). The shell script "authProg" speaks the "authpipe" protocol outlined in:

        http://www.courier-mta.org/authlib/README_authlib.html

The script takes an "AUTH" request, checks the username/password using the "chkpw_pam" program, then makes a separate connection to authdaemond to make a "PRE" request to return the account data. Since authProg simply sends "FAIL" for a "PRE" request, the request falls through to the authuserdb module.

This simple idea works and required almost no programming, but I'd like to know if there's a nicer way to do this.

        Cheers,

        -- Johnny Lam <[EMAIL PROTECTED]>
#!/bin/sh

auth()
{
        chkpwprog=/etc/pkg/authlib/chkpw_pam
        hoseprog=/usr/pkg/bin/hose
        socketfile=/var/authdaemon/socket
        [ -x $chkpwprog ] || { fail; return; }
        [ -x $hoseprog ] ||  { fail; return; }
        [ -S $socketfile ] || { fail; return; }

        # Read the AUTH data from stdin.
        read service junk
        read authtype junk; [ "$authtype" = "login" ] || { fail; return; }
        read username junk
        read password

        # Authenticate the user/password combination.
        CHKPW_PASSWD="$password"; export CHKPW_PASSWD
        $chkpwprog $service $username || { fail; return; }

        # Make a PRE request to authdaemond so that the next module's
        # results will return.  Filter out PASSWD* lines since they
        # won't apply, given that the correct passwords are stored in
        # the service checked by $chkpwprog.
        #
        echo "PRE . $service $username" |
        $hoseprog localhost $socketfile --unix --slave |
        while read line; do
                case $line in
                PASSWD=*|PASSWD2=*)     ;;
                .)      echo "."; break ;;
                *)      echo "$line"    ;;
                esac
        done
}

# Set the other actions to fail so that authdaemond will fall through and
# try the next module on the authmodulelist.
#
pre()           { fail; }
passwd()        { fail; }
enumerate()     { echo "."; }
fail()          { echo "FAIL"; }

# Read the first line of standard input to figure out which action should
# be taken.
#
read stdin
echo "          Read: $stdin"
case $stdin in
"AUTH "*)       auth ;;
"PRE . "*)      pre ;;
"PASSWD "*)     passwd ;;
"ENUMERATE")    enumerate ;;
*)              fail ;;
esac
exit 0
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <security/pam_appl.h>

static int
pam_nullconv(int n,
        const struct pam_message **msg,
        struct pam_response **resp,
        void *data)
{
        return PAM_CONV_ERR;
}

static struct pam_conv pamc = { pam_nullconv, NULL };

int
main(int argc, char *argv[])
{
        pam_handle_t *pamh = NULL;
        int pam_err = PAM_ABORT;
        const char *service;
        const char *user;
        const char *pass;

        if (argc != 3)
                goto cleanup;

        service = argv[1];
        user = argv[2];

        /*
         * Grab the password for the user from the CHKPW_PASSWD environment
         * variable.
         */
        if ((pass = getenv("CHKPW_PASSWD")) == NULL)
                goto cleanup;
        if ((pam_err = pam_start(service, user, &pamc, &pamh)) != PAM_SUCCESS)
                goto cleanup;
        if ((pam_err = pam_set_item(pamh, PAM_AUTHTOK, pass)) != PAM_SUCCESS)
                goto cleanup;
        if ((pam_err = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
                goto cleanup;

 cleanup:
        if (pamh != NULL) {
                pam_end(pamh, pam_err);
        }

        /* Return 0 for a success, and 1 for everything else. */
        return (pam_err == PAM_SUCCESS ? 0 : 1);
}

Reply via email to