mirror of
https://github.com/falk-werner/webfuse
synced 2024-10-27 20:34:10 +00:00
add example of PAM authenticator
This commit is contained in:
parent
7a131d6024
commit
7559bebb05
1
example/authenticator/pam/.gitignore
vendored
Normal file
1
example/authenticator/pam/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build/
|
26
example/authenticator/pam/CMakeLists.txt
Normal file
26
example/authenticator/pam/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(webfuse VERSION 2.0.0)
|
||||
|
||||
option(WITHOUT_CLANG_TIDY "Disables clang tidy" OFF)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_library(PAM pam REQUIRED)
|
||||
find_library(B64 b64 REQUIRED)
|
||||
|
||||
add_executable(webfuse_pam_authenticator src/main.cpp)
|
||||
target_link_libraries(webfuse_pam_authenticator pam b64)
|
||||
install(TARGETS webfuse_pam_authenticator DESTINATION bin)
|
||||
install(FILES etc/pam.d/webfuse DESTINATION /etc/pam.d)
|
||||
|
||||
add_executable(webfuse_pam_token_encode src/token_encode.cpp)
|
||||
target_link_libraries(webfuse_pam_token_encode b64)
|
||||
|
||||
add_executable(webfuse_pam_token_decode src/token_decode.cpp)
|
||||
target_link_libraries(webfuse_pam_token_decode b64)
|
||||
|
||||
if(NOT(WITHOUT_CLANG_TIDY))
|
||||
set_property(
|
||||
TARGET webfuse_pam_authenticator webfuse_pam_token_encode webfuse_pam_token_decode
|
||||
PROPERTY CXX_CLANG_TIDY clang-tidy -checks=readability-*,-readability-identifier-length -warnings-as-errors=*)
|
||||
endif()
|
31
example/authenticator/pam/README.md
Normal file
31
example/authenticator/pam/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# webfuse PAM authenticator
|
||||
|
||||
This directory contains an example of a webfuse authenticator using PAM.
|
||||
|
||||
The authenticator uses `username` and `password` for authentication.
|
||||
Since webfuse only provides a token, username and password are encoded as follows:
|
||||
|
||||
TOKEN := base64 ( USERNAME ":" PASSWORD )
|
||||
|
||||
Example:
|
||||
|
||||
USERNAME := "user"
|
||||
PASSWORD := "secret"
|
||||
TOKEN := base64 ( "user:secret" ) = "XNlcjpzZWNyZXQ="
|
||||
|
||||
The utilities `webfuse_pam_token_encode` and `webfuse_pam_token_decode` can be used
|
||||
to encode and decode tokens.
|
||||
|
||||
## Build
|
||||
|
||||
cmake -b build
|
||||
cmake build
|
||||
|
||||
## Dependencies
|
||||
|
||||
- libpam
|
||||
- libb64
|
||||
|
||||
## Notes
|
||||
|
||||
- in order to make the authenticator work, read access to /etc/shadow is needed
|
7
example/authenticator/pam/etc/pam.d/webfuse
Normal file
7
example/authenticator/pam/etc/pam.d/webfuse
Normal file
@ -0,0 +1,7 @@
|
||||
# Use unix authentication for webfuse authentication.
|
||||
#
|
||||
# NOTE:
|
||||
# - in order to use unix authentication, read access to /etc/shadow is required
|
||||
|
||||
auth required pam_unix.so
|
||||
account required pam_unix.so
|
188
example/authenticator/pam/src/main.cpp
Normal file
188
example/authenticator/pam/src/main.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
#include <b64/decode.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool decode(std::string const & token, std::string & username, std::string & password)
|
||||
{
|
||||
std::istringstream encoded(token);
|
||||
std::stringstream decoded;
|
||||
|
||||
base64::decoder decoder;
|
||||
decoder.decode(encoded, decoded);
|
||||
auto const plain = decoded.str();
|
||||
auto const pos = plain.find(':');
|
||||
bool const success = (pos != std::string::npos);
|
||||
if (success)
|
||||
{
|
||||
username = plain.substr(0, pos);
|
||||
password = plain.substr(pos + 1);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
struct credentials
|
||||
{
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
extern "C" int conversation(
|
||||
int count,
|
||||
struct pam_message const ** messages,
|
||||
struct pam_response ** ret_responses,
|
||||
void * user_data)
|
||||
{
|
||||
if (count <= 0) { return PAM_CONV_ERR; }
|
||||
|
||||
int result = PAM_SUCCESS;
|
||||
auto * responses = reinterpret_cast<struct pam_response *>(malloc(count * sizeof(struct pam_response)));
|
||||
auto * creds = reinterpret_cast<credentials*>(user_data);
|
||||
|
||||
for(int i = 0; (PAM_SUCCESS == result) && (i < count); i++)
|
||||
{
|
||||
auto * response = &responses[i];
|
||||
auto const * message = messages[i];
|
||||
|
||||
response->resp_retcode = 0;
|
||||
response->resp = nullptr;
|
||||
|
||||
switch(message->msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
response->resp = strdup(creds->username.c_str());
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
response->resp = strdup(creds->password.c_str());
|
||||
break;
|
||||
case PAM_TEXT_INFO:
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
break;
|
||||
default:
|
||||
free(responses);
|
||||
result = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (PAM_SUCCESS == result)
|
||||
{
|
||||
*ret_responses = responses;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool authenticate(std::string const & username, std::string const & password)
|
||||
{
|
||||
credentials creds;
|
||||
creds.username = username;
|
||||
creds.password = password;
|
||||
|
||||
struct pam_conv conv;
|
||||
conv.conv = &conversation;
|
||||
conv.appdata_ptr = reinterpret_cast<void*>(&creds);
|
||||
|
||||
pam_handle_t * handle = nullptr;
|
||||
bool cleanup_handle = false;
|
||||
bool result = true;
|
||||
{
|
||||
auto const rc = pam_start("webfuse", nullptr, &conv, &handle);
|
||||
result = (PAM_SUCCESS == rc);
|
||||
cleanup_handle = result;
|
||||
if (!result)
|
||||
{
|
||||
syslog(LOG_AUTH, "failed to start PAM conversation");
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
pam_set_item(handle, PAM_USER, reinterpret_cast<void const*>(username.c_str()));
|
||||
auto const rc = pam_authenticate(handle, PAM_DISALLOW_NULL_AUTHTOK);
|
||||
result = (PAM_SUCCESS == rc);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
auto const rc = pam_acct_mgmt(handle, PAM_DISALLOW_NULL_AUTHTOK);
|
||||
result = (PAM_SUCCESS == rc);
|
||||
}
|
||||
|
||||
if (cleanup_handle)
|
||||
{
|
||||
pam_end(handle, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int exit_code = EXIT_FAILURE;
|
||||
bool print_usage = true;
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
std::string const token = argv[1];
|
||||
if (("-h" != token) && ("--help" != token))
|
||||
{
|
||||
print_usage = false;
|
||||
|
||||
openlog("webfuse_pam_auth", 0, LOG_AUTH);
|
||||
|
||||
std::string username;
|
||||
std::string password;
|
||||
auto const decode_valid = decode(token, username, password);
|
||||
if (decode_valid)
|
||||
{
|
||||
auto const is_authenticated = authenticate(username, password);
|
||||
if (is_authenticated)
|
||||
{
|
||||
syslog(LOG_AUTH, "authenticate user \"%s\"", username.c_str());
|
||||
exit_code = EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_AUTH, "failed to authenticate user \"%s\"", username.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_AUTH, "failed to decode authentication token");
|
||||
}
|
||||
|
||||
closelog();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (print_usage)
|
||||
{
|
||||
std::cout << R"(webfuse_pam_authenticator, (c) 2023 Falk Werner
|
||||
webfuse PAM authenticator
|
||||
|
||||
Usage:
|
||||
webfuse_pam_authenticator <token>
|
||||
|
||||
Arguments:
|
||||
<token> token used for authentication
|
||||
token := base64(<username> ":" <password>)
|
||||
)";
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
47
example/authenticator/pam/src/token_decode.cpp
Normal file
47
example/authenticator/pam/src/token_decode.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include <b64/decode.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int exit_code = EXIT_FAILURE;
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
std::istringstream token(argv[1]);
|
||||
|
||||
base64::decoder decoder;
|
||||
std::stringstream plain;
|
||||
decoder.decode(token, plain);
|
||||
|
||||
auto const plain_str = plain.str();
|
||||
auto const pos = plain_str.find(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto const username = plain_str.substr(0, pos);
|
||||
auto const password = plain_str.substr(pos + 1);
|
||||
|
||||
std::cout << "username: " << username << std::endl;
|
||||
std::cout << "password: " << password << std::endl;
|
||||
exit_code = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (exit_code != EXIT_SUCCESS)
|
||||
{
|
||||
std::cerr << "error: failed to decode" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << R"(webfuse_pam_token_encode, (c) 2023 Falk Werner
|
||||
Encode token for webfuse PAM authenticator
|
||||
|
||||
Usage:
|
||||
webfuse_pam_token_encode <username> <password>
|
||||
)";
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
32
example/authenticator/pam/src/token_encode.cpp
Normal file
32
example/authenticator/pam/src/token_encode.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include <b64/encode.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 3)
|
||||
{
|
||||
std::string const username = argv[1];
|
||||
std::string const password = argv[2];
|
||||
|
||||
base64::encoder encoder;
|
||||
std::istringstream plain(username + ':' + password);
|
||||
std::stringstream token;
|
||||
encoder.encode(plain, token);
|
||||
|
||||
std::cout << token.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << R"(webfuse_pam_token_encode, (c) 2023 Falk Werner
|
||||
Encode token for webfuse PAM authenticator
|
||||
|
||||
Usage:
|
||||
webfuse_pam_token_encode <username> <password>
|
||||
)";
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user