diff -ruN lighttpd-1.4.50.orig/configure.ac lighttpd-1.4.50/configure.ac --- lighttpd-1.4.50.orig/configure.ac 2018-08-12 20:07:22.000000000 -0800 +++ lighttpd-1.4.50/configure.ac 2018-09-17 10:50:50.339749492 -0800 @@ -425,6 +425,23 @@ fi AM_CONDITIONAL([BUILD_WITH_LDAP], [test "$WITH_LDAP" != no]) +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 +AM_CONDITIONAL(BUILD_WITH_PAM, test ! $WITH_PAM = no) + dnl Check for xattr AC_MSG_NOTICE([----------------------------------------]) AC_MSG_CHECKING([for extended attributes support]) @@ -1340,6 +1357,13 @@ mod_wstunnel \ " +plugins="mod_authn_pam" +if test ! "x$PAM_LIB" = x; then + do_build="$do_build $plugins" +else + no_build="$no_build $plugins" +fi + lighty_track_feature() { if eval "$3"; then enable_feature="$enable_feature $1" diff -ruN lighttpd-1.4.50.orig/src/Makefile.am lighttpd-1.4.50/src/Makefile.am --- lighttpd-1.4.50.orig/src/Makefile.am 2018-08-12 20:07:22.000000000 -0800 +++ lighttpd-1.4.50/src/Makefile.am 2018-09-17 10:50:50.346749589 -0800 @@ -342,6 +342,13 @@ mod_authn_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd) endif +if BUILD_WITH_PAM +lib_LTLIBRARIES += mod_authn_pam.la +mod_authn_pam_la_SOURCES = mod_authn_pam.c +mod_authn_pam_la_LDFLAGS = $(common_module_ldflags) +mod_authn_pam_la_LIBADD = $(PAM_LIB) $(common_libadd) +endif + if BUILD_WITH_MYSQL lib_LTLIBRARIES += mod_authn_mysql.la mod_authn_mysql_la_SOURCES = mod_authn_mysql.c diff -ruN lighttpd-1.4.50.orig/src/configfile.c lighttpd-1.4.50/src/configfile.c --- lighttpd-1.4.50.orig/src/configfile.c 2018-08-12 20:07:22.000000000 -0800 +++ lighttpd-1.4.50/src/configfile.c 2018-09-17 10:50:50.351749629 -0800 @@ -24,7 +24,7 @@ #include -#if defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)) +#if defined(HAVE_LIBPAM) || defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)) static void config_warn_authn_module (server *srv, const char *module) { size_t len = strlen(module); for (size_t i = 0; i < srv->config_context->used; ++i) { @@ -553,6 +553,7 @@ int append_mod_authn_file = 1; int append_mod_authn_ldap = 1; int append_mod_authn_mysql = 1; + int append_mod_authn_pam = 1; int append_mod_openssl = 1; int contains_mod_auth = 0; @@ -588,6 +589,10 @@ append_mod_authn_mysql = 0; } + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_pam"))) { + append_mod_authn_pam = 0; + } + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_auth"))) { contains_mod_auth = 1; } @@ -599,6 +604,7 @@ 0 == append_mod_authn_file && 0 == append_mod_authn_ldap && 0 == append_mod_authn_mysql && + 0 == append_mod_authn_pam && 1 == contains_mod_auth) { break; } diff -ruN lighttpd-1.4.50.orig/src/mod_authn_pam.c lighttpd-1.4.50/src/mod_authn_pam.c --- lighttpd-1.4.50.orig/src/mod_authn_pam.c 1969-12-31 14:00:00.000000000 -1000 +++ lighttpd-1.4.50/src/mod_authn_pam.c 2018-09-17 10:51:47.680591497 -0800 @@ -0,0 +1,205 @@ +#include "first.h" + +#define USE_PAM +#include + +#include "base.h" +#include "http_auth.h" +#include "log.h" +#include "plugin.h" + +#include +#include +#include + +typedef struct { + buffer *auth_pam_servicename; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */ +} plugin_data; + +static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_pam_init) { + static http_auth_backend_t http_auth_backend_pam = + { "pam", mod_authn_pam_basic, NULL, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_pam */ + http_auth_backend_pam.p_d = p; + http_auth_backend_set(&http_auth_backend_pam); + + return p; +} + +FREE_FUNC(mod_authn_pam_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_pam_servicename); + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) { + plugin_data *p = p_d; + size_t i; +config_values_t cv[] = { + { "auth.backend.pam.service-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + + s->auth_pam_servicename = buffer_init(); + + cv[0].destination = s->auth_pam_servicename; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_pam_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_pam_servicename); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.pam.service-name"))) { + PATCH(auth_pam_servicename); + } + } + } + + return 0; +} +#undef PATCH + +typedef struct { + plugin_config *conf; + const char *username; + const char *realm; + const char *password; +} mod_auth_pam_data; + +static int mod_authn_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; +} + +static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + pam_handle_t *pamh = NULL; + mod_auth_pam_data pam_data = { + &p->conf, + username->ptr, + require->realm->ptr, + pw + }; + struct pam_conv conv = { mod_authn_pam_fn_conv, &pam_data }; + + mod_authn_pam_patch_connection(srv, con, p); + + 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; + + pam_end(pamh, retval); + return HANDLER_GO_ON; + +auth_pam_fail: + log_error_write(srv, __FILE__, __LINE__, "ss", "pam:", pam_strerror(pamh, retval)); + pam_end(pamh, retval); + return HANDLER_ERROR; +} + +int mod_authn_pam_plugin_init(plugin *p); +int mod_authn_pam_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_pam"); + p->init = mod_authn_pam_init; + p->set_defaults = mod_authn_pam_set_defaults; + p->cleanup = mod_authn_pam_free; + + p->data = NULL; + + return 0; +} diff -ruN lighttpd-1.4.50.orig/src/server.c lighttpd-1.4.50/src/server.c --- lighttpd-1.4.50.orig/src/server.c 2018-08-12 20:07:22.000000000 -0800 +++ lighttpd-1.4.50/src/server.c 2018-09-17 10:50:50.358749597 -0800 @@ -578,6 +578,11 @@ #else "\t- DBI support\n" #endif +#ifdef HAVE_LIBPAM + "\t+ PAM support\n" +#else + "\t- PAM support\n" +#endif #ifdef HAVE_KRB5 "\t+ Kerberos support\n" #else