Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1701976
remote.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
remote.c
View Options
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/xml.h>
#include <crm/cib/internal.h>
#include "callbacks.h"
/* #undef HAVE_PAM_PAM_APPL_H */
/* #undef HAVE_GNUTLS_GNUTLS_H */
#ifdef HAVE_GNUTLS_GNUTLS_H
# undef KEYFILE
# include <gnutls/gnutls.h>
#endif
#include <pwd.h>
#include <grp.h>
#if HAVE_SECURITY_PAM_APPL_H
# include <security/pam_appl.h>
# define HAVE_PAM 1
#else
# if HAVE_PAM_PAM_APPL_H
# include <pam/pam_appl.h>
# define HAVE_PAM 1
# endif
#endif
#ifdef HAVE_DECL_NANOSLEEP
# include <time.h>
#endif
extern int remote_tls_fd;
extern gboolean cib_shutdown_flag;
int init_remote_listener(int port, gboolean encrypted);
void cib_remote_connection_destroy(gpointer user_data);
#ifdef HAVE_GNUTLS_GNUTLS_H
# define DH_BITS 1024
gnutls_dh_params dh_params;
extern gnutls_anon_server_credentials anon_cred_s;
static void
debug_log(int level, const char *str)
{
fputs(str, stderr);
}
extern gnutls_session *create_tls_session(int csock, int type);
#endif
int num_clients;
int authenticate_user(const char *user, const char *passwd);
int cib_remote_listen(gpointer data);
int cib_remote_msg(gpointer data);
static void
remote_connection_destroy(gpointer user_data)
{
return;
}
#define ERROR_SUFFIX " Shutting down remote listener"
int
init_remote_listener(int port, gboolean encrypted)
{
int ssock;
struct sockaddr_in saddr;
int optval;
static struct mainloop_fd_callbacks remote_listen_fd_callbacks =
{
.dispatch = cib_remote_listen,
.destroy = remote_connection_destroy,
};
if (port <= 0) {
/* dont start it */
return 0;
}
if (encrypted) {
#ifndef HAVE_GNUTLS_GNUTLS_H
crm_warn("TLS support is not available");
return 0;
#else
crm_notice("Starting a tls listener on port %d.", port);
gnutls_global_init();
/* gnutls_global_set_log_level (10); */
gnutls_global_set_log_function(debug_log);
gnutls_dh_params_init(&dh_params);
gnutls_dh_params_generate2(dh_params, DH_BITS);
gnutls_anon_allocate_server_credentials(&anon_cred_s);
gnutls_anon_set_server_dh_params(anon_cred_s, dh_params);
#endif
} else {
crm_warn("Starting a plain_text listener on port %d.", port);
}
#ifndef HAVE_PAM
crm_warn("PAM is _not_ enabled!");
#endif
/* create server socket */
ssock = socket(AF_INET, SOCK_STREAM, 0);
if (ssock == -1) {
crm_perror(LOG_ERR, "Can not create server socket." ERROR_SUFFIX);
return -1;
}
/* reuse address */
optval = 1;
setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
/* bind server socket */
memset(&saddr, '\0', sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons(port);
if (bind(ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
crm_perror(LOG_ERR, "Can not bind server socket." ERROR_SUFFIX);
close(ssock);
return -2;
}
if (listen(ssock, 10) == -1) {
crm_perror(LOG_ERR, "Can not start listen." ERROR_SUFFIX);
close(ssock);
return -3;
}
mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, ssock, &ssock, &remote_listen_fd_callbacks);
return ssock;
}
static int
check_group_membership(const char *usr, const char *grp)
{
int index = 0;
struct passwd *pwd = NULL;
struct group *group = NULL;
CRM_CHECK(usr != NULL, return FALSE);
CRM_CHECK(grp != NULL, return FALSE);
pwd = getpwnam(usr);
if (pwd == NULL) {
crm_err("No user named '%s' exists!", usr);
return FALSE;
}
group = getgrgid(pwd->pw_gid);
if (group != NULL && crm_str_eq(grp, group->gr_name, TRUE)) {
return TRUE;
}
group = getgrnam(grp);
if (group == NULL) {
crm_err("No group named '%s' exists!", grp);
return FALSE;
}
while (TRUE) {
char *member = group->gr_mem[index++];
if (member == NULL) {
break;
} else if (crm_str_eq(usr, member, TRUE)) {
return TRUE;
}
};
return FALSE;
}
int
cib_remote_listen(gpointer data)
{
int lpc = 0;
int csock = 0;
unsigned laddr;
time_t now = 0;
time_t start = time(NULL);
struct sockaddr_in addr;
int ssock = *(int *)data;
#ifdef HAVE_GNUTLS_GNUTLS_H
gnutls_session *session = NULL;
#endif
cib_client_t *new_client = NULL;
xmlNode *login = NULL;
const char *user = NULL;
const char *pass = NULL;
const char *tmp = NULL;
#ifdef HAVE_DECL_NANOSLEEP
const struct timespec sleepfast = { 0, 10000000 }; /* 10 millisec */
#endif
static struct mainloop_fd_callbacks remote_client_fd_callbacks =
{
.dispatch = cib_remote_msg,
.destroy = cib_remote_connection_destroy,
};
/* accept the connection */
laddr = sizeof(addr);
csock = accept(ssock, (struct sockaddr *)&addr, &laddr);
crm_debug("New %s connection from %s",
ssock == remote_tls_fd ? "secure" : "clear-text", inet_ntoa(addr.sin_addr));
if (csock == -1) {
crm_err("accept socket failed");
return TRUE;
}
if (ssock == remote_tls_fd) {
#ifdef HAVE_GNUTLS_GNUTLS_H
/* create gnutls session for the server socket */
session = create_tls_session(csock, GNUTLS_SERVER);
if (session == NULL) {
crm_err("TLS session creation failed");
close(csock);
return TRUE;
}
#endif
}
do {
crm_trace("Iter: %d", lpc++);
if (ssock == remote_tls_fd) {
#ifdef HAVE_GNUTLS_GNUTLS_H
login = crm_recv_remote_msg(session, TRUE);
#endif
} else {
login = crm_recv_remote_msg(GINT_TO_POINTER(csock), FALSE);
}
if (login != NULL) {
break;
}
#ifdef HAVE_DECL_NANOSLEEP
nanosleep(&sleepfast, NULL);
#else
sleep(1);
#endif
now = time(NULL);
/* Peers have 3s to connect */
} while (login == NULL && (start - now) < 4);
crm_log_xml_info(login, "Login: ");
if (login == NULL) {
goto bail;
}
tmp = crm_element_name(login);
if (safe_str_neq(tmp, "cib_command")) {
crm_err("Wrong tag: %s", tmp);
goto bail;
}
tmp = crm_element_value(login, "op");
if (safe_str_neq(tmp, "authenticate")) {
crm_err("Wrong operation: %s", tmp);
goto bail;
}
user = crm_element_value(login, "user");
pass = crm_element_value(login, "password");
/* Non-root daemons can only validate the password of the
* user they're running as
*/
if (check_group_membership(user, CRM_DAEMON_GROUP) == FALSE) {
crm_err("User is not a member of the required group");
goto bail;
} else if (authenticate_user(user, pass) == FALSE) {
crm_err("PAM auth failed");
goto bail;
}
/* send ACK */
num_clients++;
new_client = calloc(1, sizeof(cib_client_t));
new_client->name = crm_element_value_copy(login, "name");
CRM_CHECK(new_client->id == NULL, free(new_client->id));
new_client->id = crm_generate_uuid();
#if ENABLE_ACL
new_client->user = strdup(user);
#endif
new_client->callback_id = NULL;
if (ssock == remote_tls_fd) {
#ifdef HAVE_GNUTLS_GNUTLS_H
new_client->encrypted = TRUE;
new_client->session = session;
#endif
} else {
new_client->session = GINT_TO_POINTER(csock);
}
free_xml(login);
login = create_xml_node(NULL, "cib_result");
crm_xml_add(login, F_CIB_OPERATION, CRM_OP_REGISTER);
crm_xml_add(login, F_CIB_CLIENTID, new_client->id);
crm_send_remote_msg(new_client->session, login, new_client->encrypted);
free_xml(login);
new_client->remote = mainloop_add_fd(
"cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, &remote_client_fd_callbacks);
g_hash_table_insert(client_list, new_client->id, new_client);
return TRUE;
bail:
if (ssock == remote_tls_fd) {
#ifdef HAVE_GNUTLS_GNUTLS_H
gnutls_bye(*session, GNUTLS_SHUT_RDWR);
gnutls_deinit(*session);
gnutls_free(session);
#endif
}
close(csock);
free_xml(login);
return TRUE;
}
void
cib_remote_connection_destroy(gpointer user_data)
{
cib_client_t *client = user_data;
if (client == NULL) {
return;
}
crm_trace("Cleaning up after client disconnect: %s/%s",
crm_str(client->name), client->id);
if (client->id != NULL) {
if (!g_hash_table_remove(client_list, client->id)) {
crm_err("Client %s not found in the hashtable", client->name);
}
}
crm_trace("Destroying %s (%p)", client->name, user_data);
num_clients--;
crm_trace("Num unfree'd clients: %d", num_clients);
free(client->name);
free(client->callback_id);
free(client->id);
free(client->user);
free(client);
crm_trace("Freed the cib client");
if (cib_shutdown_flag) {
cib_shutdown(0);
}
return;
}
int
cib_remote_msg(gpointer data)
{
const char *value = NULL;
xmlNode *command = NULL;
cib_client_t *client = data;
crm_trace("%s callback", client->encrypted ? "secure" : "clear-text");
command = crm_recv_remote_msg(client->session, client->encrypted);
if (command == NULL) {
return -1;
}
value = crm_element_name(command);
if (safe_str_neq(value, "cib_command")) {
crm_log_xml_trace(command, "Bad command: ");
goto bail;
}
if (client->name == NULL) {
value = crm_element_value(command, F_CLIENTNAME);
if (value == NULL) {
client->name = strdup(client->id);
} else {
client->name = strdup(value);
}
}
if (client->callback_id == NULL) {
value = crm_element_value(command, F_CIB_CALLBACK_TOKEN);
if (value != NULL) {
client->callback_id = strdup(value);
crm_trace("Callback channel for %s is %s", client->id, client->callback_id);
} else {
client->callback_id = strdup(client->id);
}
}
/* unset dangerous options */
xml_remove_prop(command, F_ORIG);
xml_remove_prop(command, F_CIB_HOST);
xml_remove_prop(command, F_CIB_GLOBAL_UPDATE);
crm_xml_add(command, F_TYPE, T_CIB);
crm_xml_add(command, F_CIB_CLIENTID, client->id);
crm_xml_add(command, F_CIB_CLIENTNAME, client->name);
#if ENABLE_ACL
crm_xml_add(command, F_CIB_USER, client->user);
#endif
if (crm_element_value(command, F_CIB_CALLID) == NULL) {
char *call_uuid = crm_generate_uuid();
/* fix the command */
crm_xml_add(command, F_CIB_CALLID, call_uuid);
free(call_uuid);
}
if (crm_element_value(command, F_CIB_CALLOPTS) == NULL) {
crm_xml_add_int(command, F_CIB_CALLOPTS, 0);
}
crm_log_xml_trace(command, "Remote command: ");
cib_common_callback_worker(0, 0, command, client, TRUE);
bail:
free_xml(command);
command = NULL;
return 0;
}
#ifdef HAVE_PAM
/*
* Useful Examples:
* http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html
* http://developer.apple.com/samplecode/CryptNoMore/index.html
*/
static int
construct_pam_passwd(int num_msg, const struct pam_message **msg,
struct pam_response **response, void *data)
{
int count = 0;
struct pam_response *reply;
char *string = (char *)data;
CRM_CHECK(data, return PAM_CONV_ERR);
CRM_CHECK(num_msg == 1, return PAM_CONV_ERR); /* We only want to handle one message */
reply = calloc(1, sizeof(struct pam_response));
CRM_ASSERT(reply != NULL);
for (count = 0; count < num_msg; ++count) {
switch (msg[count]->msg_style) {
case PAM_TEXT_INFO:
crm_info("PAM: %s\n", msg[count]->msg);
break;
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
reply[count].resp_retcode = 0;
reply[count].resp = string; /* We already made a copy */
case PAM_ERROR_MSG:
/* In theory we'd want to print this, but then
* we see the password prompt in the logs
*/
/* crm_err("PAM error: %s\n", msg[count]->msg); */
break;
default:
crm_err("Unhandled conversation type: %d", msg[count]->msg_style);
goto bail;
}
}
*response = reply;
reply = NULL;
return PAM_SUCCESS;
bail:
for (count = 0; count < num_msg; ++count) {
if (reply[count].resp != NULL) {
switch (msg[count]->msg_style) {
case PAM_PROMPT_ECHO_ON:
case PAM_PROMPT_ECHO_OFF:
/* Erase the data - it contained a password */
while (*(reply[count].resp)) {
*(reply[count].resp)++ = '\0';
}
free(reply[count].resp);
break;
}
reply[count].resp = NULL;
}
}
free(reply);
reply = NULL;
return PAM_CONV_ERR;
}
#endif
int
authenticate_user(const char *user, const char *passwd)
{
#ifndef HAVE_PAM
gboolean pass = TRUE;
#else
int rc = 0;
gboolean pass = FALSE;
const void *p_user = NULL;
struct pam_conv p_conv;
struct pam_handle *pam_h = NULL;
static const char *pam_name = NULL;
if (pam_name == NULL) {
pam_name = getenv("CIB_pam_service");
}
if (pam_name == NULL) {
pam_name = "login";
}
p_conv.conv = construct_pam_passwd;
p_conv.appdata_ptr = strdup(passwd);
rc = pam_start(pam_name, user, &p_conv, &pam_h);
if (rc != PAM_SUCCESS) {
crm_err("Could not initialize PAM: %s (%d)", pam_strerror(pam_h, rc), rc);
goto bail;
}
rc = pam_authenticate(pam_h, 0);
if (rc != PAM_SUCCESS) {
crm_err("Authentication failed for %s: %s (%d)", user, pam_strerror(pam_h, rc), rc);
goto bail;
}
/* Make sure we authenticated the user we wanted to authenticate.
* Since we also run as non-root, it might be worth pre-checking
* the user has the same EID as us, since that the only user we
* can authenticate.
*/
rc = pam_get_item(pam_h, PAM_USER, &p_user);
if (rc != PAM_SUCCESS) {
crm_err("Internal PAM error: %s (%d)", pam_strerror(pam_h, rc), rc);
goto bail;
} else if (p_user == NULL) {
crm_err("Unknown user authenticated.");
goto bail;
} else if (safe_str_neq(p_user, user)) {
crm_err("User mismatch: %s vs. %s.", (const char *)p_user, (const char *)user);
goto bail;
}
rc = pam_acct_mgmt(pam_h, 0);
if (rc != PAM_SUCCESS) {
crm_err("Access denied: %s (%d)", pam_strerror(pam_h, rc), rc);
goto bail;
}
pass = TRUE;
bail:
rc = pam_end(pam_h, rc);
#endif
return pass;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Oct 29, 8:24 PM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
942585
Default Alt Text
remote.c (15 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment