blob: 58cce5a6229c06dc599d8787113bf17879ba9495 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/pam_utils.h"
#include <security/pam_appl.h>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/strings/cstring_view.h"
#include "remoting/base/logging.h"
namespace remoting {
namespace {
int PamConversation(int num_messages,
const struct pam_message** messages,
struct pam_response** responses,
void* context) {
// Assume we're only being asked to log messages, in which case our response
// need to be free()-able zero-initialized memory.
*responses = static_cast<struct pam_response*>(
calloc(num_messages, sizeof(struct pam_response)));
// SAFETY: `messages` is a pointer to a dynamically allocated array of
// `pam_message`s, which should be at least `num_messages` long.
// PamConversation is invoked as a callback from pam_start API, and is
// documented to have this signature:
// https://man7.org/linux/man-pages/man3/pam_start.3.html,
// https://linux.die.net/man/3/pam_conv
auto messages_span =
UNSAFE_BUFFERS(base::span(messages, static_cast<size_t>(num_messages)));
// We don't expect this function to be called. Since we have no easy way
// of returning a response, we consider it to be an error if we're asked
// for one and abort. Informational and error messages are logged.
for (const pam_message* message : messages_span) {
switch (message->msg_style) {
case PAM_ERROR_MSG:
LOG(ERROR) << "PAM conversation error message: " << message->msg;
break;
case PAM_TEXT_INFO:
HOST_LOG << "PAM conversation message: " << message->msg;
break;
default:
LOG(FATAL) << "Unexpected PAM conversation response required: "
<< message->msg << "; msg_style = " << message->msg_style;
}
}
return PAM_SUCCESS;
}
} // namespace
bool IsLocalLoginAllowed(base::cstring_view username) {
HOST_LOG << "Running local login check.";
if (username.empty()) {
LOG(ERROR) << "Username is empty.";
return false;
}
struct pam_conv conv = {PamConversation, nullptr};
pam_handle_t* handle = nullptr;
HOST_LOG << "Calling pam_start() with username " << username;
int result =
pam_start("chrome-remote-desktop", username.c_str(), &conv, &handle);
if (result != PAM_SUCCESS) {
LOG(ERROR) << "pam_start() returned error " << result;
} else {
HOST_LOG << "Calling pam_acct_mgmt()";
result = pam_acct_mgmt(handle, 0);
if (result != PAM_SUCCESS) {
LOG(ERROR) << "pam_acct_mgmt() returned error " << result;
}
}
HOST_LOG << "Calling pam_end()";
pam_end(handle, result);
HOST_LOG << "Local login check for " << username
<< (result == PAM_SUCCESS ? " succeeded." : " failed.");
return result == PAM_SUCCESS;
}
} // namespace remoting