Add PAM support

Possibilities are endless. With a simple /etc/pam.d/loolwsd config below,
the user which runs loolwsd ('lool' in production environment) can login
to admin console with normal linux password.

    auth       required     pam_unix.so
    account    required     pam_unix.so

Change-Id: I354a7e9b4705e8fe346d17d6b6041d1406198b37
Reviewed-on: https://gerrit.libreoffice.org/48307
Reviewed-by: Andras Timar <andras.timar@collabora.com>
Tested-by: Andras Timar <andras.timar@collabora.com>
private/kendy/monitoring-rebased
Andras Timar 2018-01-22 11:57:39 +01:00
parent 30b44bf373
commit a2e25cc7d3
6 changed files with 90 additions and 15 deletions

View File

@ -36,7 +36,7 @@ if !ENABLE_DEBUG
AM_CPPFLAGS += -DNDEBUG
endif
AM_LDFLAGS = -pthread -Wl,-E,-rpath,/snap/loolwsd/current/usr/lib $(ZLIB_LIBS)
AM_LDFLAGS = -pthread -Wl,-E,-rpath,/snap/loolwsd/current/usr/lib -lpam $(ZLIB_LIBS)
if ENABLE_SSL
AM_LDFLAGS += -lssl -lcrypto

2
debian/control vendored
View File

@ -1,7 +1,7 @@
Source: loolwsd
Section: web
Priority: optional
Maintainer: Tor Lillqvist <tml@collabora.com>
Maintainer: Andras Timar <andras.timar@collabora.com>
Build-Depends: debhelper (>= 9), dh-systemd (>= 1.3), libcap-dev, libcap2-bin, libpcre3-dev, libpng-dev, libpoco-dev (>= 1.7.5), linux-libc-dev
Standards-Version: 3.9.7

View File

@ -88,6 +88,9 @@ install -D -m 644 sysconfig.loolwsd %{buildroot}/var/adm/fillup-templates
mkdir -p %{buildroot}/etc/cron.d
echo "#Remove old tiles once every 10 days at midnight" > %{buildroot}/etc/cron.d/loolwsd.cron
echo "0 0 */1 * * root find /var/cache/loolwsd -name \"*.png\" -a -atime +10 -exec rm {} \;" >> %{buildroot}/etc/cron.d/loolwsd.cron
mkdir -p %{buildroot}/etc/pam.d
echo "auth required pam_unix.so" > %{buildroot}/etc/pam.d/loolwsd
echo "account required pam_unix.so" >> %{buildroot}/etc/pam.d/loolwsd
%files
/usr/bin/loolwsd
@ -116,6 +119,7 @@ echo "0 0 */1 * * root find /var/cache/loolwsd -name \"*.png\" -a -atime +10 -ex
%endif
%config(noreplace) /etc/cron.d/loolwsd.cron
%config(noreplace) /etc/pam.d/loolwsd
%config(noreplace) %attr(640, lool, root) /etc/loolwsd/loolwsd.xml
%config /etc/loolwsd/loolkitconfig.xcu

View File

@ -100,8 +100,9 @@
<tile_cache_persistent desc="Should the tiles persist between two editing sessions of the given document?" type="bool" default="true">true</tile_cache_persistent>
<admin_console desc="Web admin console settings.">
<username desc="The username of the admin console. Must be set."></username>
<password desc="The password of the admin console. Must be set."></password>
<enable_pam desc="Enable admin user authentication with PAM" type="bool" default="true">true</enable_pam>
<username desc="The username of the admin console. Must be set, if PAM is not enabled, otherwise it's optional."></username>
<password desc="The password of the admin console. Deprecated on most platforms. Instead, use loolconfig to set up a secure password."></password>
</admin_console>
</config>

View File

@ -17,6 +17,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <zlib.h>
#include <security/pam_appl.h>
#include <openssl/evp.h>
@ -53,6 +54,62 @@ using Poco::Util::Application;
std::map<std::string, std::pair<std::string, std::string>> FileServerRequestHandler::FileHash;
namespace {
int functionConversation(int /*num_msg*/, const struct pam_message** /*msg*/,
struct pam_response **reply, void *appdata_ptr)
{
*reply = (struct pam_response *)malloc(sizeof(struct pam_response));
(*reply)[0].resp = strdup(static_cast<char *>(appdata_ptr));
(*reply)[0].resp_retcode = 0;
return PAM_SUCCESS;
}
bool isPamAuthOk(const std::string user, const std::string pass)
{
struct pam_conv localConversation { functionConversation, nullptr };
pam_handle_t *localAuthHandle = NULL;
int retval;
localConversation.appdata_ptr = const_cast<char *>(pass.c_str());
retval = pam_start("loolwsd", user.c_str(), &localConversation, &localAuthHandle);
if (retval != PAM_SUCCESS)
{
LOG_ERR("pam_start returned " << retval);
return false;
}
retval = pam_authenticate(localAuthHandle, 0);
if (retval != PAM_SUCCESS)
{
if (retval == PAM_AUTH_ERR)
{
LOG_ERR("PAM authentication failure for user \"" << user << "\".");
}
else
{
LOG_ERR("pam_authenticate returned " << retval);
}
return false;
}
LOG_INF("PAM authentication success for user \"" << user << "\".");
retval = pam_end(localAuthHandle, retval);
if (retval != PAM_SUCCESS)
{
LOG_WRN("pam_end returned " << retval);
}
return true;
}
}
bool FileServerRequestHandler::isAdminLoggedIn(const HTTPRequest& request,
HTTPResponse &response)
{
@ -81,10 +138,12 @@ bool FileServerRequestHandler::isAdminLoggedIn(const HTTPRequest& request,
HTTPBasicCredentials credentials(request);
std::string userProvidedPwd = credentials.getPassword();
std::string userProvidedUsr = credentials.getUsername();
// If no cookie found, or is invalid, let admin re-login
const std::string user = config.getString("admin_console.username", "");
std::string pass = config.getString("admin_console.password", "");
const bool pam = config.getBool("admin_console.enable_pam", "true");
if (config.has("admin_console.secure_password"))
{
@ -99,9 +158,7 @@ bool FileServerRequestHandler::isAdminLoggedIn(const HTTPRequest& request,
!Util::dataFromHexString(tokens[3], saltData))
{
LOG_ERR("Incorrect format detected for secure_password in config file."
<< "Denying access until correctly set."
<< "Use loolconfig to configure admin password.");
return false;
}
unsigned char userProvidedPwdHash[tokens[4].size() / 2];
@ -120,18 +177,29 @@ bool FileServerRequestHandler::isAdminLoggedIn(const HTTPRequest& request,
#else
LOG_ERR("The config file has admin_console.secure_password setting, "
<< "but this application was compiled with old OpenSSL version, "
<< "and this setting cannot be used. Falling back to plain text password, if it is set.");
<< "and this setting cannot be used. Falling back to plain text password or to PAM, if it is set.");
#endif
}
if (user.empty() || pass.empty())
if (!pam && (user.empty() || pass.empty()))
{
LOG_ERR("Admin Console credentials missing. Denying access until set.");
return false;
}
if (credentials.getUsername() == user &&
userProvidedPwd == pass)
bool authenticated = false;
if (userProvidedUsr == user && userProvidedPwd == pass)
{
authenticated = true;
}
if (!authenticated && pam)
{
authenticated = isPamAuthOk(userProvidedUsr, userProvidedPwd);
}
if (authenticated)
{
// generate and set the cookie
JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin");
@ -143,12 +211,13 @@ bool FileServerRequestHandler::isAdminLoggedIn(const HTTPRequest& request,
cookie.setSecure(LOOLWSD::isSSLEnabled() ||
LOOLWSD::isSSLTermination());
response.addCookie(cookie);
return true;
}
else
{
LOG_INF("Wrong admin credentials.");
}
LOG_INF("Wrong admin credentials.");
return false;
return authenticated;
}
void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::MemoryInputStream& message,

View File

@ -687,7 +687,8 @@ void LOOLWSD::initialize(Application& self)
{ "logging.file.property[3]", "false" },
{ "trace[@enable]", "false" },
{ "trace.path[@compress]", "true" },
{ "trace.path[@snapshot]", "false" } };
{ "trace.path[@snapshot]", "false" },
{ "admin_console.enable_pam", "true"} };
// Set default values, in case they are missing from the config file.
AutoPtr<AppConfigMap> defConfig(new AppConfigMap(DefAppConfig));