blob: b1a70cd6e1dbbeac8eff1060812539e406f35c3c [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/user_authenticator.h"
#include <security/pam_appl.h>
#include <stdlib.h>
#include <string>
#include "base/basictypes.h"
namespace remoting {
namespace {
// Class to perform a single PAM user authentication.
//
// TODO(lambroslambrou): As pam_authenticate() can be blocking, this needs to
// expose an asynchronous API, with pam_authenticate() called in a background
// thread.
class UserAuthenticatorPam : public UserAuthenticator {
public:
UserAuthenticatorPam() {}
virtual ~UserAuthenticatorPam() {}
virtual bool Authenticate(const std::string& username,
const std::string& password);
private:
// Conversation function passed to PAM as a callback.
static int ConvFunction(int num_msg,
const pam_message** msg,
pam_response** resp,
void* appdata_ptr);
// Store these for the PAM conversation function.
std::string username_;
std::string password_;
DISALLOW_COPY_AND_ASSIGN(UserAuthenticatorPam);
};
const char kPamServiceName[] = "chromoting";
bool UserAuthenticatorPam::Authenticate(const std::string& username,
const std::string& password) {
username_ = username;
password_ = password;
pam_conv conversation;
conversation.conv = ConvFunction;
conversation.appdata_ptr = static_cast<void*>(this);
// TODO(lambroslambrou): Allow PAM service name to be configurable.
pam_handle_t* pam_handle;
if (pam_start(kPamServiceName, username_.c_str(),
&conversation, &pam_handle) != PAM_SUCCESS) {
return false;
}
// TODO(lambroslambrou): Move to separate thread.
int pam_status = pam_authenticate(pam_handle, 0);
pam_end(pam_handle, pam_status);
return pam_status == PAM_SUCCESS;
}
// static
int UserAuthenticatorPam::ConvFunction(int num_msg,
const pam_message** msg,
pam_response** resp,
void* appdata_ptr) {
if (num_msg <= 0)
return PAM_CONV_ERR;
UserAuthenticatorPam* user_auth =
static_cast<UserAuthenticatorPam*>(appdata_ptr);
// Must allocate with malloc(), as the calling PAM module will
// release the memory with free().
pam_response* resp_tmp = static_cast<pam_response*>(
malloc(num_msg * sizeof(pam_response)));
if (resp_tmp == NULL)
return PAM_CONV_ERR;
bool raise_error = false;
// On exit from the loop, 'count' will hold the number of initialised items
// that the cleanup code needs to look at, in case of error.
int count;
for (count = 0; count < num_msg; count++) {
// Alias for readability.
pam_response* resp_item = &resp_tmp[count];
resp_item->resp_retcode = 0;
resp_item->resp = NULL;
switch (msg[count]->msg_style) {
case PAM_PROMPT_ECHO_ON:
resp_item->resp = strdup(user_auth->username_.c_str());
if (resp_item->resp == NULL)
raise_error = true;
break;
case PAM_PROMPT_ECHO_OFF:
resp_item->resp = strdup(user_auth->password_.c_str());
if (resp_item->resp == NULL)
raise_error = true;
break;
case PAM_TEXT_INFO:
// No response needed, as this instructs the PAM client to display
// text to the user. Leave as NULL and continue with next prompt.
break;
default:
// Unexpected style code, so abort.
raise_error = true;
}
if (raise_error)
break;
}
if (raise_error) {
// Not passing the response back, so free up any memory used.
for (int n = 0; n < count; n++) {
if (resp_tmp[n].resp) {
free(resp_tmp[n].resp);
}
}
free(resp_tmp);
return PAM_CONV_ERR;
} else {
*resp = resp_tmp;
return PAM_SUCCESS;
}
}
} // namespace
// static
UserAuthenticator* UserAuthenticator::Create() {
return new UserAuthenticatorPam();
}
} // namespace remoting