new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / win32 / agent_auth.c
diff --git a/src/win32/agent_auth.c b/src/win32/agent_auth.c
new file mode 100644 (file)
index 0000000..a4e5db0
--- /dev/null
@@ -0,0 +1,479 @@
+#define SECURITY_WIN32
+#include <windef.h>
+#include <sspi.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <winsock2.h>
+#include <schannel.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include "headers/shared.h"
+#include "debug_op.h"
+#include "file_op.h"
+#include "os_net/os_net.h"
+#include "os_regex/os_regex.h"
+#include "defs.h"
+#include "addagent/manage_agents.h"
+
+#define IO_BUFFER_SIZE  0x10000
+
+void report_help()
+{
+    printf("\n%s %s: Connects to the manager to extract the agent key.\n", __ossec_name, ARGV0);
+    printf("Available options:\n");
+    printf("\t-h                  This help message.\n");
+    printf("\t-m <manager ip>     Manager IP Address.\n");
+    printf("\t-p <port>           Manager port (default 1515).\n");
+    printf("\t-A <agent name>     Agent name (default is the hostname).\n");
+    printf("\t-P <pass>           Authorization password.\n");
+    exit(1);
+}
+
+void SendSecurityToken(const int socket, SecBuffer *OutBuffers)
+{
+    int sent = 0;
+
+    if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+    {
+        sent = send(socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+        if (sent <= 0)
+            ErrorExit("%s: Could not send security token to server (is ossec-authd running ?)", ARGV0);
+
+        // Free Output Buffer
+        FreeContextBuffer(OutBuffers[0].pvBuffer);
+        OutBuffers[0].pvBuffer = NULL;
+        OutBuffers[0].cbBuffer = 0;
+    }
+}
+
+void CreateSecureConnection(char *manager, char *port, int *socket, CtxtHandle *context, CredHandle *cred)
+{
+    SECURITY_STATUS status;
+    SCHANNEL_CRED auth_cred;
+    DWORD input_flags = 0;
+    DWORD output_flags = 0;
+    DWORD read = 0;
+    DWORD total_read = 0;
+    SecBufferDesc OutBuffer;
+    SecBuffer OutBuffers[1];
+    SecBufferDesc InBuffer;
+    SecBuffer InBuffers[2];
+    PCHAR buffer = NULL;
+
+    // Get manager IP address
+    
+    manager = OS_GetHost(manager, 3);
+    if (manager == NULL)
+        ErrorExit("%s: Could not resolve manager's hostname", ARGV0);
+
+
+
+    // Connect via TCP
+    
+    *socket = OS_ConnectTCP(port, manager);
+    
+    if (socket == 0)
+        ErrorExit("%s: Unable to connect to %s:%s", ARGV0, manager, port);
+       
+
+
+    // Setting authentication credentials
+    
+    ZeroMemory(&auth_cred, sizeof (auth_cred));
+    auth_cred.dwVersion = SCHANNEL_CRED_VERSION;
+    auth_cred.dwSessionLifespan = 60000;
+    auth_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_NO_SERVERNAME_CHECK;
+
+    status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_cred, NULL, NULL, cred, NULL);
+    if (status != SEC_E_OK)
+        ErrorExit("%s: Could not acquire credentials (AcquireCredentialsHandle failed with error code 0x%lX", ARGV0, status);
+
+
+    //
+    // Initialize security context
+    
+    OutBuffers[0].pvBuffer   = NULL;
+    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+    OutBuffers[0].cbBuffer   = 0;
+
+    OutBuffer.cBuffers = 1;
+    OutBuffer.pBuffers = OutBuffers;
+    OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+    InBuffers[1].pvBuffer = NULL;
+    InBuffers[1].cbBuffer = 0;
+    InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+    buffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
+    if (buffer == NULL)
+        ErrorExit("%s: out of memory !", ARGV0);
+
+    input_flags = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM;
+    status = InitializeSecurityContext(cred, NULL, NULL, input_flags, 0, 0, NULL, 0, context, &OutBuffer, &output_flags, NULL);
+
+    while (status != SEC_E_OK)
+    {
+        // See if we have a token to send to the server
+        if (status == SEC_I_CONTINUE_NEEDED)
+        {
+            SendSecurityToken(*socket, OutBuffers);
+            total_read = 0;
+        }
+
+        // See if we have data to retrieve from server
+        if ((total_read == 0) || (status == SEC_E_INCOMPLETE_MESSAGE))
+        {
+            read = recv(*socket, buffer + total_read, IO_BUFFER_SIZE - total_read, 0);
+            if (read <= 0)
+                ErrorExit("%s: Could not get security token from server", ARGV0);
+
+            total_read += read;
+        }
+
+        InBuffers[0].pvBuffer = buffer;
+        InBuffers[0].cbBuffer = total_read;
+        InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+        InBuffers[1].pvBuffer = NULL;
+        InBuffers[1].cbBuffer = 0;
+        InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+        InBuffer.cBuffers = 2;
+        InBuffer.pBuffers = InBuffers;
+        InBuffer.ulVersion = SECBUFFER_VERSION;
+
+        status = InitializeSecurityContext(cred, context, NULL, input_flags, 0, 0, &InBuffer, 0, context, &OutBuffer, &output_flags, NULL);
+    }
+
+    // Send remaining tokens if any
+    SendSecurityToken(*socket, OutBuffers);
+
+    printf("INFO: Connected to %s:%s\n", manager, port);
+    LocalFree(buffer);
+}
+
+void SendSecureMessage(const int socket, CtxtHandle *context, const char *format, ...)
+{
+    va_list args;
+    char *buffer;
+    unsigned int buffer_length = 0;
+    unsigned int msg_length = 0;
+    int sent = 0;
+    SecPkgContext_StreamSizes sizes;
+    SECURITY_STATUS status;
+    SecBufferDesc msg;
+    SecBuffer msg_buffers[4];
+
+    va_start(args, format);
+
+    // Get sizes for given context
+    status = QueryContextAttributes(context, SECPKG_ATTR_STREAM_SIZES, &sizes);
+    if (status != SEC_E_OK)
+        ErrorExit("%s: Could not get message sizes (QueryContextAttributes failed with error code 0x%lX)", ARGV0, status);
+
+    // Construct message
+    buffer_length = sizes.cbHeader + sizes.cbMaximumMessage + sizes.cbTrailer;
+    buffer = LocalAlloc(LMEM_FIXED, buffer_length);
+    if (buffer == NULL)
+        ErrorExit("%s: out of memory !", ARGV0);
+    vsnprintf(buffer + sizes.cbHeader, buffer_length - sizes.cbHeader, format, args);
+    msg_length = strlen(buffer + sizes.cbHeader);
+
+    // Encrypt message in place
+    msg_buffers[0].pvBuffer = buffer;
+    msg_buffers[0].cbBuffer = sizes.cbHeader;
+    msg_buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+    msg_buffers[1].pvBuffer = buffer + sizes.cbHeader;
+    msg_buffers[1].cbBuffer = msg_length;
+    msg_buffers[1].BufferType = SECBUFFER_DATA;
+
+    msg_buffers[2].pvBuffer = buffer + sizes.cbHeader + msg_length;
+    msg_buffers[2].cbBuffer = sizes.cbTrailer;
+    msg_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+    msg_buffers[3].BufferType = SECBUFFER_EMPTY;
+
+    msg.ulVersion = SECBUFFER_VERSION;
+    msg.cBuffers = 4;
+    msg.pBuffers = msg_buffers;
+
+    status = EncryptMessage(context, 0, &msg, 0);
+    if (status != SEC_E_OK)
+        ErrorExit("%s: Could not encrypt message (EncryptMessage failed with error code %lX)", ARGV0, status);
+
+    sent = send(socket, buffer, msg_buffers[0].cbBuffer + msg_buffers[1].cbBuffer + msg_buffers[2].cbBuffer, 0);
+    if (sent <= 0)
+            ErrorExit("%s: Could not send message to server", ARGV0);
+
+    va_end(args);
+}
+
+char *ReceiveSecureMessage(const int socket, CtxtHandle *context)
+{
+    char *buffer;
+    unsigned int buffer_length = 0;
+    int read = 0;
+    int i = 0;
+    char has_extra_data = 0;
+    SECURITY_STATUS status = SEC_E_INCOMPLETE_MESSAGE;
+    SecBufferDesc msg;
+    SecBuffer msg_buffers[4];
+
+    buffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
+
+    while ((status == SEC_E_INCOMPLETE_MESSAGE) || (has_extra_data))
+    {
+        if (status == SEC_E_INCOMPLETE_MESSAGE)
+        {
+            read = recv(socket, buffer + buffer_length, IO_BUFFER_SIZE - buffer_length, 0);
+            if (read <= 0)
+                ErrorExit("%s: Could not receive message from server (or invalid password)", ARGV0);
+
+            buffer_length += read;
+        }
+
+        msg_buffers[0].pvBuffer = buffer;
+        msg_buffers[0].cbBuffer = buffer_length;
+        msg_buffers[0].BufferType = SECBUFFER_DATA;
+
+        msg_buffers[1].BufferType = SECBUFFER_EMPTY;
+        msg_buffers[2].BufferType = SECBUFFER_EMPTY;
+        msg_buffers[3].BufferType = SECBUFFER_EMPTY;
+
+        msg.ulVersion = SECBUFFER_VERSION;
+        msg.cBuffers = 4;
+        msg.pBuffers = msg_buffers;
+
+        status = DecryptMessage(context, &msg, 0, NULL);
+
+        if ((status != SEC_E_OK) && (status != SEC_E_INCOMPLETE_MESSAGE))
+            ErrorExit("%s: Could not decrypt received message (DecryptMessage failed with error code 0x%lX)", ARGV0, status);
+
+        if (status == SEC_E_OK)
+        {
+            has_extra_data = 0;
+            for (i = 1; i < 4; ++i)
+                if (msg_buffers[i].BufferType == SECBUFFER_EXTRA)
+                {
+                    has_extra_data = 1;
+                    memcpy(buffer, msg_buffers[i].pvBuffer, msg_buffers[i].cbBuffer);
+                    buffer_length = msg_buffers[i].cbBuffer;
+                }
+        }
+    }
+
+    for (i = 1; i < 4; ++i)
+        if (msg_buffers[i].BufferType == SECBUFFER_DATA)
+            return msg_buffers[i].pvBuffer;
+
+    return NULL;
+}
+
+void InstallAuthKeys(char *msg)
+{
+    if (strncmp(msg, "ERROR", 5) == 0)
+        ErrorExit("%s: %s (from manager)", ARGV0, msg);
+    else if (strncmp(msg, "OSSEC K:'", 9) == 0)
+    {
+        char *key;
+        char *tmpstr;
+        char **entry;
+        FILE *fp;
+
+        printf("INFO: Received response with agent key\n");
+
+        key = msg + 9;
+        tmpstr = strchr(key, '\'');
+
+        if (!tmpstr)
+            ErrorExit("%s: Invalid key received. Closing connection.", ARGV0);
+
+        *tmpstr = '\0';
+        entry = OS_StrBreak(' ', key, 4);
+
+        if (!OS_IsValidID(entry[0]) || !OS_IsValidName(entry[1]) ||
+            !OS_IsValidName(entry[2]) || !OS_IsValidName(entry[3]))
+            ErrorExit("%s: Invalid key received (2). Closing connection.", ARGV0);
+
+        fp = fopen(KEYSFILE_PATH, "w");
+
+        if (!fp)
+            ErrorExit("%s: Unable to open key file: %s", ARGV0, KEYSFILE_PATH);
+
+        fprintf(fp, "%s\n", key);
+        fclose(fp);
+
+        printf("INFO: Valid key created. Finished.\n");
+    }
+    else
+        ErrorExit("%s: Unknown message received (%s)", ARGV0, msg);
+}
+
+void DisconnectFromServer(const int socket, CtxtHandle *context, CredHandle *cred)
+{
+    SecBufferDesc OutBuffer;
+    SecBuffer OutBuffers[1];
+    DWORD dwType;
+    SECURITY_STATUS status;
+    DWORD input_flags;
+    DWORD output_flags;
+    int sent = 0;
+
+    dwType = SCHANNEL_SHUTDOWN;
+
+    OutBuffers[0].pvBuffer   = &dwType;
+    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+    OutBuffers[0].cbBuffer   = sizeof(dwType);
+
+    OutBuffer.cBuffers  = 1;
+    OutBuffer.pBuffers  = OutBuffers;
+    OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+    status = ApplyControlToken(context, &OutBuffer);
+    if (status != SEC_E_OK)
+        ErrorExit("%s: Could not correclty close connection", ARGV0);
+
+    input_flags = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM;
+    OutBuffers[0].pvBuffer   = NULL;
+    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+    OutBuffers[0].cbBuffer   = 0;
+
+    OutBuffer.cBuffers  = 1;
+    OutBuffer.pBuffers  = OutBuffers;
+    OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+    status = InitializeSecurityContext(cred, context, NULL, input_flags, 0, 0, NULL, 0, context, &OutBuffer, &output_flags, NULL);
+    if (status != SEC_E_OK)
+        ErrorExit("%s: Could not correclty close connection (2)", ARGV0);
+
+    sent = send(socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+    if (sent <= 0)
+        ErrorExit("%s: Could not correclty close connection (3)", ARGV0);
+
+    FreeContextBuffer(OutBuffers[0].pvBuffer);
+    DeleteSecurityContext(context);
+    close(socket);
+}
+
+int main(int argc, char **argv)
+{
+    int error = 0;
+    int socket = 0;
+    char *port = "1515";
+    char c = 0;
+    char *manager = NULL;
+    char *agentname = NULL;
+    char hostname[512];
+    char *msg = NULL;
+    char *authpass = NULL;
+    char buf[4096 + 1] = { '\0' };
+    WSADATA wsa;
+    CtxtHandle context;
+    CredHandle cred;
+
+    /* Setting the name */
+    OS_SetName(ARGV0);
+
+    while((c = getopt(argc, argv, "hm:p:A:P:")) != -1)
+    {
+        switch(c){
+            case 'h':
+                report_help();
+                break;
+            case 'm':
+               if(!optarg)
+                    ErrorExit("%s: -%c needs an argument",ARGV0, c);
+                manager = optarg;
+                break;
+            case 'A':
+               if(!optarg)
+                    ErrorExit("%s: -%c needs an argument",ARGV0, c);
+                agentname = optarg;
+                break;
+            case 'p': {
+               if(!optarg)
+                    ErrorExit("%s: -%c needs an argument",ARGV0, c);
+                int tmp_port;
+                tmp_port = atoi(optarg);
+                if(tmp_port <= 0 || tmp_port >= 65536)
+                {
+                    ErrorExit("%s: Invalid port: %s", ARGV0, optarg);
+                }
+                port = optarg;
+                break;
+            }
+            case 'P':
+                if (!optarg)
+                    ErrorExit("%s: -%c needs an argument", ARGV0, c);
+
+                authpass = optarg;
+                break;
+            default:
+                report_help();
+                break;
+        }
+    }
+
+    // Initialize Windows Networking
+    error = WSAStartup(MAKEWORD(2, 2), &wsa);
+    if (error)
+        ErrorExit("%s: Could not initialize networking (WSAStartup failed with error code %u)", ARGV0, error);
+
+    // Determine agent_name
+    if(agentname == NULL)
+    {
+        if(gethostname(hostname, 512) != 0)
+            ErrorExit("%s: ERROR: Unable to extract hostname. Custom agent name not set.", ARGV0);
+
+        agentname = hostname;
+    }
+
+    /* Checking if there is a custom password file */
+    if (authpass == NULL) {
+        FILE *fp;
+        fp = fopen(AUTHDPASS_PATH, "r");
+        buf[0] = '\0';
+
+        if (fp) {
+            buf[4096] = '\0';
+            char *ret = fgets(buf, 4095, fp);
+
+            if (ret && strlen(buf) > 2) {
+                authpass = buf;
+            }
+
+            fclose(fp);
+            printf("INFO: Using password specified on file: %s\n", AUTHDPASS_PATH);
+        }
+    }
+    if (!authpass) {
+        printf("WARN: No authentication password provided. Insecure mode started.\n");
+
+    }
+
+    // Connect to socket and init security context
+    CreateSecureConnection(manager, port, &socket, &context, &cred);
+    
+    printf("INFO: Using agent name as: %s\n", agentname);
+
+    // Send request
+
+    if (authpass)
+        SendSecureMessage(socket, &context, "OSSEC PASS: %s OSSEC A:'%s'\n", authpass, agentname);
+    else
+        SendSecureMessage(socket, &context, "OSSEC A:'%s'\n", agentname);
+
+    printf("INFO: Sent request to manager. Waiting for reply.\n");
+
+    // Get response
+    msg = ReceiveSecureMessage(socket, &context);
+
+    // Install received keys
+    InstallAuthKeys(msg);
+
+    // Disconnect
+    DisconnectFromServer(socket, &context, &cred);
+
+    return (0);
+}