1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
// 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
|