diff -wbBur dbmail-2.2.10/configure.in dbmail-2.2.10.pam/configure.in --- dbmail-2.2.10/configure.in 2008-03-24 17:49:33.000000000 +0300 +++ dbmail-2.2.10.pam/configure.in 2008-09-18 16:43:04.000000000 +0400 @@ -78,6 +78,13 @@ AC_SUBST(CRYPTLIB) +dnl Check for PAM +AC_SUBST(PAMLIBS,"") +AC_CHECK_HEADERS(security/pam_appl.h, + [AC_CHECK_LIB(pam,pam_start, + [AC_DEFINE(HAVE_PAM,1,[Define if you have PAN including devel headers]) + PAMLIBS="-lpam"],,)]) + AC_SUBST(MYSQLLIB) AC_SUBST(MYSQLALIB) AC_SUBST(MYSQLLTLIB) diff -wbBur dbmail-2.2.10/dbmail-user.c dbmail-2.2.10.pam/dbmail-user.c --- dbmail-2.2.10/dbmail-user.c 2008-03-24 17:49:33.000000000 +0300 +++ dbmail-2.2.10.pam/dbmail-user.c 2008-09-18 16:43:04.000000000 +0400 @@ -157,7 +157,7 @@ "md5", "md5-raw", "md5sum", "md5sum-raw", "md5-hash", "md5-hash-raw", "md5-digest", "md5-digest-raw", "md5-base64", "md5-base64-raw", "md5base64", "md5base64-raw", - "shadow", "", NULL + "shadow", "pam", "", NULL }; /* These must correspond to the easy text names. */ @@ -166,7 +166,7 @@ MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW, MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW, MD5_BASE64, MD5_BASE64_RAW, MD5_BASE64, MD5_BASE64_RAW, - SHADOW, PLAINTEXT, PWTYPE_NULL + SHADOW, PWTYPE_PAM, PLAINTEXT, PWTYPE_NULL }; memset(pw, 0, 50); @@ -251,6 +251,12 @@ *enctype = "crypt"; } break; +#ifdef HAVE_PAM + case PWTYPE_PAM: + null_strncpy(pw, passwd, 49); + *enctype = "pam"; + break; +#endif default: qerrorf("Error: password type not supported [%s].\n", passwdtype); diff -wbBur dbmail-2.2.10/dbmail-user.h dbmail-2.2.10.pam/dbmail-user.h --- dbmail-2.2.10/dbmail-user.h 2008-03-24 17:49:33.000000000 +0300 +++ dbmail-2.2.10.pam/dbmail-user.h 2008-09-18 16:43:04.000000000 +0400 @@ -34,7 +34,7 @@ typedef enum { PLAINTEXT = 0, PLAINTEXT_RAW, CRYPT, CRYPT_RAW, MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW, - MD5_BASE64, MD5_BASE64_RAW, SHADOW, PWTYPE_NULL + MD5_BASE64, MD5_BASE64_RAW, SHADOW, PWTYPE_PAM, PWTYPE_NULL } pwtype_t; int mkpassword(const char * const user, const char * const passwd, diff -wbBur dbmail-2.2.10/modules/authsql.c dbmail-2.2.10.pam/modules/authsql.c --- dbmail-2.2.10/modules/authsql.c 2008-03-24 17:49:33.000000000 +0300 +++ dbmail-2.2.10.pam/modules/authsql.c 2008-09-18 16:43:04.000000000 +0400 @@ -27,6 +27,19 @@ #include "dbmail.h" #define THIS_MODULE "auth" +#ifdef HAVE_PAM +#include + +#ifndef DEFAULT_DBMAIL_PAM_SERVICE +#define DEFAULT_DBMAIL_PAM_SERVICE "dbmail" +#endif + +#ifndef DEFAULT_DBMAIL_PAM_TTL +#define DEFAULT_DBMAIL_PAM_TTL 60 +#endif + +#endif + extern db_param_t _db_params; #define DBPFX _db_params.pfx @@ -49,17 +62,80 @@ */ static int __auth_query(const char *thequery); +#ifdef HAVE_PAM + +static char *pam_password = NULL; /* Workaround for Solaris 2.6 brokenness */ +static pam_handle_t *pamh = NULL; +static int pam_ttl = DEFAULT_DBMAIL_PAM_TTL; +static char *pam_service = DEFAULT_DBMAIL_PAM_SERVICE; +static time_t pamh_created = 0; +/* + * A simple "conversation" function returning the supplied password. + * Has a bit to much error control, but this is my first PAM application + * so I'd rather check everything than make any mistakes. The function + * expects a single converstation message of type PAM_PROMPT_ECHO_OFF. + */ +static int +password_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) +{ + if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) { + TRACE(TRACE_ERROR, "Unexpected PAM converstaion '%d/%s'", msg[0]->msg_style, msg[0]->msg); + return PAM_CONV_ERR; + } + if (!appdata_ptr) { + /* Workaround for Solaris 2.6 where the PAM library is broken + * and does not pass appdata_ptr to the conversation routine + */ + appdata_ptr = pam_password; + } + if (!appdata_ptr) { + TRACE(TRACE_ERROR, "ERROR: No password available to password_converstation!"); + return PAM_CONV_ERR; + } + *resp = calloc(num_msg, sizeof(struct pam_response)); + if (!*resp) { + TRACE(TRACE_ERROR, "Out of memory!"); + return PAM_CONV_ERR; + } + (*resp)[0].resp = strdup((char *) appdata_ptr); + (*resp)[0].resp_retcode = 0; + + return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR); +} + +static struct pam_conv conv = +{ + &password_conversation, + NULL +}; + +#endif + + int auth_connect() { /* this function is only called after a connection has been made * if, in the future this is not the case, db.h should export a * function that enables checking for the database connection */ +#ifdef HAVE_PAM + +#endif return 0; } int auth_disconnect() { +#ifdef HAVE_PAM + int retval=PAM_SUCCESS; + if (pamh) { + retval = pam_end(pamh, retval); + if (retval != PAM_SUCCESS) { + pamh = NULL; + TRACE(TRACE_ERROR, "failed to release PAM authenticator"); + } + } +#endif return 0; } @@ -458,7 +534,71 @@ is_validated = (strncmp(md5str, query_result, 32) == 0) ? 1 : 0; g_free(md5str); } +#ifdef HAVE_PAM + else if (strcasecmp(query_result, "pam") == 0) { + int retval=0; + TRACE(TRACE_DEBUG, "validating using pam for user [%s] pass:[%s]",real_username,password); + conv.appdata_ptr = (char *) password; + pam_password= password; + if (pam_ttl == 0) { + /* Create PAM connection */ + retval = pam_start(pam_service, real_username, &conv, &pamh); + if (retval != PAM_SUCCESS) { + TRACE(TRACE_ERROR, "failed to create PAM authenticator"); + goto pam_error; + } + } else if (!pamh || (time(NULL) - pamh_created) >= pam_ttl || pamh_created > time(NULL)) { + /* Close previous PAM connection */ + if (pamh) { + retval = pam_end(pamh, retval); + if (retval != PAM_SUCCESS) { + TRACE(TRACE_WARNING, "failed to release PAM authenticator"); + } + pamh = NULL; + } + /* Initialize persistent PAM connection */ + retval = pam_start(pam_service, "dbmail@", &conv, &pamh); + if (retval != PAM_SUCCESS) { + TRACE(TRACE_ERROR, "failed to create PAM authenticator"); + goto pam_error; + } + pamh_created = time(NULL); + } + retval = PAM_SUCCESS; + if (pam_ttl != 0) { + if (retval == PAM_SUCCESS) + retval = pam_set_item(pamh, PAM_USER, real_username); + if (retval == PAM_SUCCESS) + retval = pam_set_item(pamh, PAM_CONV, &conv); + } + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); + if (retval == PAM_SUCCESS ) //&& !no_acct_mgmt + retval = pam_acct_mgmt(pamh, 0); + if (retval == PAM_SUCCESS) { + is_validated=1; + } else { +pam_error: + is_validated=0; + } + /* cleanup */ + retval = PAM_SUCCESS; +#ifdef PAM_AUTHTOK + if (pam_ttl != 0) { + if (retval == PAM_SUCCESS) + retval = pam_set_item(pamh, PAM_AUTHTOK, NULL); + } +#endif + if (pam_ttl == 0 || retval != PAM_SUCCESS) { + retval = pam_end(pamh, retval); + if (retval != PAM_SUCCESS) { + TRACE(TRACE_WARNING, "failed to release PAM authenticator\n"); + } + pamh = NULL; + } + } +#endif if (is_validated) { db_user_log_login(*user_idnr); } else { diff -wbBur dbmail-2.2.10/modules/Makefile.am dbmail-2.2.10.pam/modules/Makefile.am --- dbmail-2.2.10/modules/Makefile.am 2008-03-24 17:49:33.000000000 +0300 +++ dbmail-2.2.10.pam/modules/Makefile.am 2008-09-18 16:44:53.000000000 +0400 @@ -60,7 +60,7 @@ # This one is always built. libauth_sql_la_SOURCES = authsql.c -libauth_sql_la_LIBADD = @CRYPTLIB@ +libauth_sql_la_LIBADD = @CRYPTLIB@ @PAMLIBS@ if LDAP libauth_ldap_la_SOURCES = authldap.c