From 1f93264972a7cb3070e86de5a476de1dec8b1456 Mon Sep 17 00:00:00 2001
From: Stanislav Ravas <ravas@tind.sk>
Date: Thu, 7 Aug 2014 14:08:26 +0200
Subject: [PATCH] pam_auth: PAM authentification support
---
configure.ac | 23 ++++++++++++++++++
src/Makefile.am | 2 +-
src/http_auth.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/http_auth.h | 17 +++++++++++++-
src/mod_auth.c | 12 +++++++++-
5 files changed, 120 insertions(+), 4 deletions(-)
@@ -212,6 +212,22 @@ if test "$WITH_LDAP" != "no"; then
AC_SUBST(LBER_LIB)
fi
+dnl Check for PAM
+AC_MSG_CHECKING(for PAM support)
+AC_ARG_WITH(pam, AC_HELP_STRING([--with-pam],[enable PAM support]),
+[WITH_PAM=$withval], [WITH_PAM=no])
+AC_MSG_RESULT([$withval])
+if test "$WITH_PAM" != "no"; then
+ AC_CHECK_LIB(pam, pam_start, [
+ AC_CHECK_HEADERS([security/pam_appl.h],[
+ PAM_LIB=-lpam
+ AC_DEFINE([HAVE_LIBPAM], [1], [libpam])
+ AC_DEFINE([HAVE_SECURITY_PAM_APPL_H], [1])
+ ])
+ ])
+ AC_SUBST(PAM_LIB)
+fi
+
dnl Check for xattr
AC_MSG_CHECKING(for extended attributes support)
AC_ARG_WITH(attr, AC_HELP_STRING([--with-attr],[enable extended attribute support]),
@@ -723,6 +739,13 @@ else
disable_feature="$disable_feature $features"
fi
+features="auth-pam"
+if test ! "x$PAM_LIB" = x; then
+ enable_feature="$enable_feature $features"
+else
+ disable_feature="$disable_feature $features"
+fi
+
features="network-openssl"
if test ! "x$SSL_LIB" = x; then
enable_feature="$enable_feature $features"
@@ -243,7 +243,7 @@ mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd)
lib_LTLIBRARIES += mod_auth.la
mod_auth_la_SOURCES = mod_auth.c http_auth.c
mod_auth_la_LDFLAGS = -module -export-dynamic -avoid-version
-mod_auth_la_LIBADD = $(CRYPT_LIB) $(SSL_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd)
+mod_auth_la_LIBADD = $(CRYPT_LIB) $(SSL_LIB) $(LDAP_LIB) $(LBER_LIB) $(PAM_LIB) $(common_libadd)
lib_LTLIBRARIES += mod_rewrite.la
mod_rewrite_la_SOURCES = mod_rewrite.c
@@ -315,6 +315,8 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
stream_close(&f);
} else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
ret = 0;
+ } else if (p->conf.auth_backend == AUTH_BACKEND_PAM) {
+ ret = 0;
} else {
return -1;
}
@@ -629,6 +631,35 @@ static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
}
#endif
+#ifdef USE_PAM
+// appdata_ptr shall point to mod_auth_pam_data structure
+static int pam_fn_conv(int num_msg, const struct pam_message **msg, struct pam_response **p_resp, void *appdata_ptr) {
+ mod_auth_pam_data *p_pam = (mod_auth_pam_data *)appdata_ptr;
+ struct pam_response *resp = (struct pam_response *)malloc(num_msg * sizeof(struct pam_response));
+
+ for(int i = 0; i < num_msg; ++i) {
+ switch(msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ resp[i].resp = strdup(p_pam->password);
+ resp[i].resp_retcode = 0;
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ default:
+ // ignore
+ resp[i].resp_retcode = 0;
+ resp[i].resp = NULL;
+ continue;
+ }
+ }
+
+ *p_resp = resp;
+
+ return PAM_SUCCESS;
+}
+#endif
+
/**
*
*
@@ -834,7 +865,44 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p
return 0;
#endif
- }
+ } else if (p->conf.auth_backend == AUTH_BACKEND_PAM) {
+#ifdef USE_PAM
+ pam_handle_t *pamh = NULL;
+ mod_auth_pam_data pam_data = {
+ &p->conf,
+ username->ptr,
+ realm->ptr,
+ pw
+ };
+ struct pam_conv conv = { pam_fn_conv, &pam_data };
+
+ int retval = pam_start(
+ !buffer_is_empty(p->conf.auth_pam_servicename) ? p->conf.auth_pam_servicename->ptr : "lighttpd",
+ username->ptr,
+ &conv,
+ &pamh);
+
+ if(retval != PAM_SUCCESS)
+ goto auth_pam_fail;
+
+ if((retval = pam_authenticate(pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS)
+ goto auth_pam_fail;
+
+ if((retval = pam_acct_mgmt(pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS)
+ goto auth_pam_fail;
+
+ if (p->conf.auth_debug > 1)
+ log_error_write(srv, __FILE__, __LINE__, "ss", "pam: authenticated as", username->ptr);
+
+ pam_end(pamh, retval);
+ return 0;
+
+auth_pam_fail:
+ log_error_write(srv, __FILE__, __LINE__, "ss", "pam:", pam_strerror(pamh, retval));
+ pam_end(pamh, retval);
+ return -1;
+#endif
+ }
return -1;
}
@@ -9,12 +9,18 @@
# include <ldap.h>
#endif
+#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_LIBPAM)
+# define USE_PAM
+# include <security/pam_appl.h>
+#endif
+
typedef enum {
AUTH_BACKEND_UNSET,
AUTH_BACKEND_PLAIN,
AUTH_BACKEND_LDAP,
AUTH_BACKEND_HTPASSWD,
- AUTH_BACKEND_HTDIGEST
+ AUTH_BACKEND_HTDIGEST,
+ AUTH_BACKEND_PAM
} auth_backend_t;
typedef struct {
@@ -38,6 +44,8 @@ typedef struct {
unsigned short auth_ldap_starttls;
unsigned short auth_ldap_allow_empty_pw;
+ buffer *auth_pam_servicename;
+
unsigned short auth_debug;
/* generated */
@@ -66,6 +74,13 @@ typedef struct {
mod_auth_plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */
} mod_auth_plugin_data;
+typedef struct {
+ mod_auth_plugin_config *conf;
+ const char *username;
+ const char *realm;
+ const char *password;
+} mod_auth_pam_data;
+
int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str);
int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str);
int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char hh[33]);
@@ -76,6 +76,8 @@ FREE_FUNC(mod_auth_free) {
buffer_free(s->auth_ldap_filter);
buffer_free(s->auth_ldap_cafile);
+ buffer_free(s->auth_pam_servicename);
+
#ifdef USE_LDAP
buffer_free(s->ldap_filter_pre);
buffer_free(s->ldap_filter_post);
@@ -114,6 +116,7 @@ static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plug
PATCH(auth_ldap_cafile);
PATCH(auth_ldap_starttls);
PATCH(auth_ldap_allow_empty_pw);
+ PATCH(auth_pam_servicename);
#ifdef USE_LDAP
p->anon_conf = s;
PATCH(ldap_filter_pre);
@@ -169,7 +172,9 @@ static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plug
PATCH(auth_ldap_bindpw);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) {
PATCH(auth_ldap_allow_empty_pw);
- }
+ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.pam.service-name"))) {
+ PATCH(auth_pam_servicename);
+ }
}
}
@@ -361,6 +366,7 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) {
{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
{ "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
+ { "auth.backend.pam.service-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
@@ -386,6 +392,7 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) {
s->auth_ldap_filter = buffer_init();
s->auth_ldap_cafile = buffer_init();
s->auth_ldap_starttls = 0;
+ s->auth_pam_servicename = buffer_init();
s->auth_debug = 0;
s->auth_require = array_init();
@@ -411,6 +418,7 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) {
cv[12].destination = s->auth_htdigest_userfile;
cv[13].destination = s->auth_htpasswd_userfile;
cv[14].destination = &(s->auth_debug);
+ cv[15].destination = s->auth_pam_servicename;
p->config_storage[i] = s;
ca = ((data_config *)srv->config_context->data[i])->value;
@@ -428,6 +436,8 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) {
s->auth_backend = AUTH_BACKEND_PLAIN;
} else if (0 == strcmp(s->auth_backend_conf->ptr, "ldap")) {
s->auth_backend = AUTH_BACKEND_LDAP;
+ } else if (0 == strcmp(s->auth_backend_conf->ptr, "pam")) {
+ s->auth_backend = AUTH_BACKEND_PAM;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf);
--
1.7.10.4