|
|
|
|
/*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|