Files
digFTP/src/plugins/auth_pam/auth_pam.cpp
Wirlaburla 5ff35c26ab * Added plugin support
* Improved Filer (LocalFiler)
* Supports Explicit SSL/TLS with OpenSSL/Crypto
* Fixed most of the bugs that drove me nuts.
* Properly handled socket closes
* Improved socket cleanup
* Buffered file downloads
* Lots 'o errors!

I did it again! I made a single commit with everything!
2024-12-13 10:51:10 -06:00

131 lines
3.5 KiB
C++

#include "auth_plugin.h"
#include <security/pam_appl.h>
#include <pwd.h>
#include <grp.h>
#include <string>
#include <cstring>
#include <memory>
class PAMAuthPlugin : public Auth {
private:
std::string service_name;
uid_t user_uid;
gid_t user_gid;
// PAM Conversation function
static int pam_conv_func(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr) {
const char* password = static_cast<const char*>(appdata_ptr);
if (!password) return PAM_CONV_ERR;
// Allocate memory for responses
*resp = static_cast<pam_response*>(calloc(num_msg, sizeof(struct pam_response)));
if (*resp == nullptr) return PAM_CONV_ERR;
// Handle messages
for (int i = 0; i < num_msg; i++) {
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
(*resp)[i].resp = strdup(password);
if ((*resp)[i].resp == nullptr) {
for (int j = 0; j < i; j++) {
free((*resp)[j].resp);
}
free(*resp);
*resp = nullptr;
return PAM_CONV_ERR;
}
(*resp)[i].resp_retcode = 0;
} else {
(*resp)[i].resp = nullptr;
(*resp)[i].resp_retcode = 0;
}
}
return PAM_SUCCESS;
}
public:
PAMAuthPlugin() : service_name("ftpd") {}
virtual bool initialize(const std::map<std::string, std::string>& config) override {
// Get PAM service name with default
auto service_it = config.find("pam_service");
service_name = (service_it != config.end()) ? service_it->second : "ftpd";
auto chroot_it = config.find("chroot");
this->setChroot((chroot_it != config.end()) ?
(chroot_it->second == "on" || chroot_it->second == "true" || chroot_it->second == "yes") :
true);
return true;
}
virtual bool authenticate(ClientAuthDetails* auth_data) override {
if (!auth_data || !auth_data->username[0] || !auth_data->password[0]) {
logger->print(LOGLEVEL_ERROR, "auth_pam: Cannot use empty auth data");
return false;
}
struct pam_conv conv;
conv.conv = pam_conv_func;
conv.appdata_ptr = static_cast<void*>(auth_data->password);
pam_handle_t* pamh = nullptr;
// Start PAM session
int retval = pam_start(service_name.c_str(), auth_data->username, &conv, &pamh);
if (retval != PAM_SUCCESS) {
logger->print(LOGLEVEL_ERROR, "auth_pam: Failed to start PAM: %s",
pamh ? pam_strerror(pamh, retval) : "Unknown error");
if (pamh) pam_end(pamh, retval);
return false;
}
// Authenticate user
retval = pam_authenticate(pamh, 0);
if (retval != PAM_SUCCESS) {
logger->print(LOGLEVEL_ERROR, "auth_pam: Authentication failed: %s",
pam_strerror(pamh, retval));
pam_end(pamh, retval);
return false;
}
// Check account validity
retval = pam_acct_mgmt(pamh, 0);
if (retval != PAM_SUCCESS) {
logger->print(LOGLEVEL_ERROR, "auth_pam: Account validation failed: %s",
pam_strerror(pamh, retval));
pam_end(pamh, retval);
return false;
}
// End PAM session
pam_end(pamh, PAM_SUCCESS);
// Get user info
struct passwd* pw = getpwnam(auth_data->username);
if (!pw) {
logger->print(LOGLEVEL_ERROR, "auth_pam: Failed to get user info for %s",
auth_data->username);
return false;
}
// Store user info
user_uid = pw->pw_uid;
user_gid = pw->pw_gid;
// Set home directory
if (pw->pw_dir) {
strncpy(auth_data->home_dir, pw->pw_dir, sizeof(auth_data->home_dir) - 1);
auth_data->home_dir[sizeof(auth_data->home_dir) - 1] = '\0';
}
return true;
}
virtual bool isPasswordRequired() override {
return true;
}
};
IMPLEMENT_AUTH_PLUGIN(PAMAuthPlugin, "pam", "PAM-based local authentication", "1.0.0")