You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
8.9 KiB

/*
* Remote Laboratory Authentication Server
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (c) 2012 Timothy Pearson
* Raptor Engineering
* http://www.raptorengineeringinc.com
*/
#include <stdlib.h>
#include "auth_conn.h"
/*
The AuthSocket class provides a socket that is connected with a client.
For every client that connects to the server, the server creates a new
instance of this class.
*/
AuthSocket::AuthSocket(int sock, TQObject *parent, const char *name) :
TQSocket( parent, name ) {
iplocal = NULL;
ipremote = NULL;
searchpath = NULL;
service = "remotefpga";
localdomain = NULL;
userdomain = NULL;
conn = NULL;
line = 0;
connect(this, SIGNAL(connectionClosed()), SLOT(deleteLater()));
connect(this, SIGNAL(connectionClosed()), SLOT(connectionClosedHandler()));
setSocket( sock );
}
AuthSocket::~AuthSocket() {
//
}
void AuthSocket::close() {
TQSocket::close();
connectionClosedHandler();
}
void AuthSocket::connectionClosedHandler() {
printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii());
}
int AuthSocket::initiateKerberosHandshake() {
return authenticate_connection_with_kerberos(socket());
}
#define NET_SEC_BUF_SIZE (2048)
static int sasl_my_log(void *context __attribute__((unused)), int priority, const char *message) {
const char *label;
if (!message) {
return SASL_BADPARAM;
}
switch (priority) {
case SASL_LOG_ERR:
label = "Error";
break;
case SASL_LOG_NOTE:
label = "Info";
break;
default:
label = "Other";
break;
}
printf("[SASL %s] %s\n\r", label, message);
return SASL_OK;
}
sasl_callback_t callbacks[] = {
{SASL_CB_LOG, (sasl_callback_ft)&sasl_my_log, NULL},
{SASL_CB_LIST_END, NULL, NULL}
};
void AuthSocket::free_conn(void) {
if (conn) {
sasl_dispose(&conn);
}
}
void AuthSocket::send_sasl_data_to_network(const char *buffer, unsigned length, int netfd)
{
char *buf;
unsigned len, alloclen;
int result;
alloclen = ((length / 3) + 1) * 4 + 1;
buf = (char*)malloc(alloclen);
if (!buf) {
printf("[ERROR] Unable to malloc()!\n\r");
return;
}
result = sasl_encode64(buffer, length, buf, alloclen, &len);
if (result != SASL_OK) {
printf("[ERROR] Encoding data in base64 returned %s (%d)\n\r", sasl_errdetail(conn), result);
return;
}
len = strlen(buf);
buf[len] = '\n';
buf[len+1] = 0;
write(netfd, buf, len+1);
free(buf);
}
unsigned int AuthSocket::get_sasl_data_from_network(char *buf) {
unsigned int len;
int result;
len = 0;
while (1) {
tqApp->processEvents();
if (state() != TQSocket::Connected) {
return -1;
}
if (readBlock(buf+len, 1) > 0) {
if (buf[len] == '\n') {
buf[len] = 0;
break;
}
if (buf[len] != '\r') {
len++;
}
}
if (len >= NET_SEC_BUF_SIZE) {
break;
}
}
len = strlen(buf);
result = sasl_decode64(buf, (unsigned) strlen(buf), buf, NET_SEC_BUF_SIZE, &len);
if (result != SASL_OK) {
printf("[ERROR] Decoding data from base64 returned %s (%d)\n\r", sasl_errdetail(conn), result);
return -1;
}
buf[len] = '\0';
return len;
}
int AuthSocket::write_data_to_client(int fd, const char* readbuf, int cc) {
int result = 0;
unsigned int len;
const char *data;
result=sasl_encode(conn, readbuf, cc, &data, &len);
if (result != SASL_OK) {
printf("[ERROR] Encrypting data returned %s (%d)\n\r", sasl_errdetail(conn), result);
return -1;
}
send_sasl_data_to_network(data, len, fd);
return 0;
}
int AuthSocket::receive_data_from_client(char *buf, int netfd) {
unsigned int recv_len;
const char *recv_data;
int result;
int len;
len = get_sasl_data_from_network(buf);
if (len >= 0) {
result=sasl_decode(conn, buf, len, &recv_data, &recv_len);
if (result != SASL_OK) {
printf("[ERROR] Decrypting data returned %s (%d)\n\r", sasl_errdetail(conn), result);
return -1;
}
strncpy(buf, recv_data, NET_SEC_BUF_SIZE);
}
return 0;
}
int AuthSocket::authenticate_connection_with_kerberos(int netfd) {
char buf[NET_SEC_BUF_SIZE];
int result = 0;
int serverlast = 0;
sasl_security_properties_t secprops;
const char *ext_authid = NULL;
unsigned int len;
int count;
const char *data;
char user_authorized = 0;
sasl_ssf_t *ssf;
// FIXME
// Initialize default data structures
memset(&secprops, 0L, sizeof(secprops));
secprops.maxbufsize = NET_SEC_BUF_SIZE;
secprops.max_ssf = UINT_MAX;
result = sasl_server_init(callbacks, "remotefpga");
if (result != SASL_OK) {
printf("[ERROR] Initializing libsasl returned %s (%d)\n\r", sasl_errdetail(conn), result);
return -1;
}
result = sasl_server_new(service, localdomain, userdomain, iplocal, ipremote, NULL, serverlast, &conn);
if (result != SASL_OK) {
printf("[ERROR] Allocating sasl connection state returned %s (%d)\n\r", sasl_errdetail(conn), result);
return -1;
}
result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
if (result != SASL_OK) {
printf("[ERROR] Setting security properties returned %s (%d)\n\r", sasl_errdetail(conn), result);
free_conn();
return -1;
}
puts("[DEBUG] Generating client mechanism list...");
result = sasl_listmech(conn, ext_authid, NULL, " ", NULL, &data, &len, &count);
if (result != SASL_OK) {
printf("[ERROR] Generating client mechanism list returned %s (%d)\n\r", sasl_errdetail(conn), result);
free_conn();
return -1;
}
printf("[DEBUG] Sending list of %d mechanism(s)\n\r", count);
send_sasl_data_to_network(data, len, netfd);
printf("[DEBUG] Waiting for client mechanism...\n\r");
len = get_sasl_data_from_network(buf);
if (strlen(buf) < len) {
printf("[DEBUG] Initial response received (%d < %d) [%s]\n\r", strlen(buf), len, buf);
// An initial response is present
data = buf + strlen(buf) + 1;
len = len - (unsigned) strlen(buf) - 1;
}
else {
data = NULL;
len = 0;
}
result = sasl_server_start(conn, buf, data, len, &data, &len);
if (result != SASL_OK && result != SASL_CONTINUE) {
printf("[ERROR] Starting SASL negotiation returned %s (%d)\n\r", sasl_errdetail(conn), result);
free_conn();
return -1;
}
while (result == SASL_CONTINUE) {
if (data) {
printf("[DEBUG] Sending response...\n\r");
send_sasl_data_to_network(data, len, netfd);
}
else {
printf("[ERROR] No data to send!\n\r");
free_conn();
return -1;
}
printf("[DEBUG] Waiting for client reply...\n\r");
len = get_sasl_data_from_network(buf);
data = NULL;
result = sasl_server_step(conn, buf, len, &data, &len);
if (result != SASL_OK && result != SASL_CONTINUE) {
printf("[ERROR] Performing SASL negotiation returned %s (%d)\n\r", sasl_errdetail(conn), result);
free_conn();
return -1;
}
}
printf("[DEBUG] Negotiation complete\n\r");
if(serverlast && data) {
printf("[DEBUG] Additional information needed to be sent\n\r");
send_sasl_data_to_network(data, len, netfd);
}
result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data);
if (result != SASL_OK) {
printf("[WARNING] Unable to determine authenticated username!\n\r");
}
else {
printf("[DEBUG] Authenticated username: %s\n\r", data ? data : "(NULL)");
}
result = sasl_getprop(conn, SASL_DEFUSERREALM, (const void **)&data);
if (result != SASL_OK) {
printf("[WARNING] Unable to determine authenticated realm!\n\r");
}
else {
printf("[DEBUG] Authenticated realm: %s\n\r", data ? data : "(NULL)");
}
result = sasl_getprop(conn, SASL_SSF, (const void **)&ssf);
if (result != SASL_OK) {
printf("[WARNING] Unable to determine SSF!\n\r");
}
else {
printf("[DEBUG] Authenticated SSF: %d\n", *ssf);
}
// RAJA FIXME
if (user_authorized == 1) {
// Send list of available servers...
write_data_to_client(netfd, "OK<EFBFBD>", strlen("OK<EFBFBD>"));
}
write_data_to_client(netfd, "TESTING", strlen("TESTING"));
return 0;
}
/*
The AuthServer class handles new connections to the server. For every
client that connects, it creates a new AuthSocket -- that instance is now
responsible for the communication with that client.
*/
AuthServer::AuthServer(TQObject* parent) :
TQServerSocket( 4004, 1, parent ) {
if ( !ok() ) {
printf("[ERROR] Failed to bind to port 4004\n\r");
exit(1);
}
}
AuthServer::~AuthServer() {
//
}
void AuthServer::newConnection(int socket) {
AuthSocket *s = new AuthSocket(socket, this);
s->m_remoteHost = s->peerAddress().toString();
printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii());
if (s->initiateKerberosHandshake() != 0) {
s->close();
}
else {
emit newConnect(s);
}
}