new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / win32 / agent_auth.c
1 #define SECURITY_WIN32
2 #include <windef.h>
3 #include <sspi.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <winsock2.h>
7 #include <schannel.h>
8 #include <unistd.h>
9 #include <stdarg.h>
10 #include "headers/shared.h"
11 #include "debug_op.h"
12 #include "file_op.h"
13 #include "os_net/os_net.h"
14 #include "os_regex/os_regex.h"
15 #include "defs.h"
16 #include "addagent/manage_agents.h"
17
18 #define IO_BUFFER_SIZE  0x10000
19
20 void report_help()
21 {
22     printf("\n%s %s: Connects to the manager to extract the agent key.\n", __ossec_name, ARGV0);
23     printf("Available options:\n");
24     printf("\t-h                  This help message.\n");
25     printf("\t-m <manager ip>     Manager IP Address.\n");
26     printf("\t-p <port>           Manager port (default 1515).\n");
27     printf("\t-A <agent name>     Agent name (default is the hostname).\n");
28     printf("\t-P <pass>           Authorization password.\n");
29     exit(1);
30 }
31
32 void SendSecurityToken(const int socket, SecBuffer *OutBuffers)
33 {
34     int sent = 0;
35
36     if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
37     {
38         sent = send(socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
39         if (sent <= 0)
40             ErrorExit("%s: Could not send security token to server (is ossec-authd running ?)", ARGV0);
41
42         // Free Output Buffer
43         FreeContextBuffer(OutBuffers[0].pvBuffer);
44         OutBuffers[0].pvBuffer = NULL;
45         OutBuffers[0].cbBuffer = 0;
46     }
47 }
48
49 void CreateSecureConnection(char *manager, char *port, int *socket, CtxtHandle *context, CredHandle *cred)
50 {
51     SECURITY_STATUS status;
52     SCHANNEL_CRED auth_cred;
53     DWORD input_flags = 0;
54     DWORD output_flags = 0;
55     DWORD read = 0;
56     DWORD total_read = 0;
57     SecBufferDesc OutBuffer;
58     SecBuffer OutBuffers[1];
59     SecBufferDesc InBuffer;
60     SecBuffer InBuffers[2];
61     PCHAR buffer = NULL;
62
63     // Get manager IP address
64     
65     manager = OS_GetHost(manager, 3);
66     if (manager == NULL)
67         ErrorExit("%s: Could not resolve manager's hostname", ARGV0);
68
69
70
71     // Connect via TCP
72     
73     *socket = OS_ConnectTCP(port, manager);
74     
75     if (socket == 0)
76         ErrorExit("%s: Unable to connect to %s:%s", ARGV0, manager, port);
77         
78
79
80     // Setting authentication credentials
81     
82     ZeroMemory(&auth_cred, sizeof (auth_cred));
83     auth_cred.dwVersion = SCHANNEL_CRED_VERSION;
84     auth_cred.dwSessionLifespan = 60000;
85     auth_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_NO_SERVERNAME_CHECK;
86
87     status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_cred, NULL, NULL, cred, NULL);
88     if (status != SEC_E_OK)
89         ErrorExit("%s: Could not acquire credentials (AcquireCredentialsHandle failed with error code 0x%lX", ARGV0, status);
90
91
92     //
93     // Initialize security context
94     
95     OutBuffers[0].pvBuffer   = NULL;
96     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
97     OutBuffers[0].cbBuffer   = 0;
98
99     OutBuffer.cBuffers = 1;
100     OutBuffer.pBuffers = OutBuffers;
101     OutBuffer.ulVersion = SECBUFFER_VERSION;
102
103     InBuffers[1].pvBuffer = NULL;
104     InBuffers[1].cbBuffer = 0;
105     InBuffers[1].BufferType = SECBUFFER_EMPTY;
106
107     buffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
108     if (buffer == NULL)
109         ErrorExit("%s: out of memory !", ARGV0);
110
111     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;
112     status = InitializeSecurityContext(cred, NULL, NULL, input_flags, 0, 0, NULL, 0, context, &OutBuffer, &output_flags, NULL);
113
114     while (status != SEC_E_OK)
115     {
116         // See if we have a token to send to the server
117         if (status == SEC_I_CONTINUE_NEEDED)
118         {
119             SendSecurityToken(*socket, OutBuffers);
120             total_read = 0;
121         }
122
123         // See if we have data to retrieve from server
124         if ((total_read == 0) || (status == SEC_E_INCOMPLETE_MESSAGE))
125         {
126             read = recv(*socket, buffer + total_read, IO_BUFFER_SIZE - total_read, 0);
127             if (read <= 0)
128                 ErrorExit("%s: Could not get security token from server", ARGV0);
129
130             total_read += read;
131         }
132
133         InBuffers[0].pvBuffer = buffer;
134         InBuffers[0].cbBuffer = total_read;
135         InBuffers[0].BufferType = SECBUFFER_TOKEN;
136
137         InBuffers[1].pvBuffer = NULL;
138         InBuffers[1].cbBuffer = 0;
139         InBuffers[1].BufferType = SECBUFFER_EMPTY;
140
141         InBuffer.cBuffers = 2;
142         InBuffer.pBuffers = InBuffers;
143         InBuffer.ulVersion = SECBUFFER_VERSION;
144
145         status = InitializeSecurityContext(cred, context, NULL, input_flags, 0, 0, &InBuffer, 0, context, &OutBuffer, &output_flags, NULL);
146     }
147
148     // Send remaining tokens if any
149     SendSecurityToken(*socket, OutBuffers);
150
151     printf("INFO: Connected to %s:%s\n", manager, port);
152     LocalFree(buffer);
153 }
154
155 void SendSecureMessage(const int socket, CtxtHandle *context, const char *format, ...)
156 {
157     va_list args;
158     char *buffer;
159     unsigned int buffer_length = 0;
160     unsigned int msg_length = 0;
161     int sent = 0;
162     SecPkgContext_StreamSizes sizes;
163     SECURITY_STATUS status;
164     SecBufferDesc msg;
165     SecBuffer msg_buffers[4];
166
167     va_start(args, format);
168
169     // Get sizes for given context
170     status = QueryContextAttributes(context, SECPKG_ATTR_STREAM_SIZES, &sizes);
171     if (status != SEC_E_OK)
172         ErrorExit("%s: Could not get message sizes (QueryContextAttributes failed with error code 0x%lX)", ARGV0, status);
173
174     // Construct message
175     buffer_length = sizes.cbHeader + sizes.cbMaximumMessage + sizes.cbTrailer;
176     buffer = LocalAlloc(LMEM_FIXED, buffer_length);
177     if (buffer == NULL)
178         ErrorExit("%s: out of memory !", ARGV0);
179     vsnprintf(buffer + sizes.cbHeader, buffer_length - sizes.cbHeader, format, args);
180     msg_length = strlen(buffer + sizes.cbHeader);
181
182     // Encrypt message in place
183     msg_buffers[0].pvBuffer = buffer;
184     msg_buffers[0].cbBuffer = sizes.cbHeader;
185     msg_buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
186
187     msg_buffers[1].pvBuffer = buffer + sizes.cbHeader;
188     msg_buffers[1].cbBuffer = msg_length;
189     msg_buffers[1].BufferType = SECBUFFER_DATA;
190
191     msg_buffers[2].pvBuffer = buffer + sizes.cbHeader + msg_length;
192     msg_buffers[2].cbBuffer = sizes.cbTrailer;
193     msg_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
194
195     msg_buffers[3].BufferType = SECBUFFER_EMPTY;
196
197     msg.ulVersion = SECBUFFER_VERSION;
198     msg.cBuffers = 4;
199     msg.pBuffers = msg_buffers;
200
201     status = EncryptMessage(context, 0, &msg, 0);
202     if (status != SEC_E_OK)
203         ErrorExit("%s: Could not encrypt message (EncryptMessage failed with error code %lX)", ARGV0, status);
204
205     sent = send(socket, buffer, msg_buffers[0].cbBuffer + msg_buffers[1].cbBuffer + msg_buffers[2].cbBuffer, 0);
206     if (sent <= 0)
207             ErrorExit("%s: Could not send message to server", ARGV0);
208
209     va_end(args);
210 }
211
212 char *ReceiveSecureMessage(const int socket, CtxtHandle *context)
213 {
214     char *buffer;
215     unsigned int buffer_length = 0;
216     int read = 0;
217     int i = 0;
218     char has_extra_data = 0;
219     SECURITY_STATUS status = SEC_E_INCOMPLETE_MESSAGE;
220     SecBufferDesc msg;
221     SecBuffer msg_buffers[4];
222
223     buffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
224
225     while ((status == SEC_E_INCOMPLETE_MESSAGE) || (has_extra_data))
226     {
227         if (status == SEC_E_INCOMPLETE_MESSAGE)
228         {
229             read = recv(socket, buffer + buffer_length, IO_BUFFER_SIZE - buffer_length, 0);
230             if (read <= 0)
231                 ErrorExit("%s: Could not receive message from server (or invalid password)", ARGV0);
232
233             buffer_length += read;
234         }
235
236         msg_buffers[0].pvBuffer = buffer;
237         msg_buffers[0].cbBuffer = buffer_length;
238         msg_buffers[0].BufferType = SECBUFFER_DATA;
239
240         msg_buffers[1].BufferType = SECBUFFER_EMPTY;
241         msg_buffers[2].BufferType = SECBUFFER_EMPTY;
242         msg_buffers[3].BufferType = SECBUFFER_EMPTY;
243
244         msg.ulVersion = SECBUFFER_VERSION;
245         msg.cBuffers = 4;
246         msg.pBuffers = msg_buffers;
247
248         status = DecryptMessage(context, &msg, 0, NULL);
249
250         if ((status != SEC_E_OK) && (status != SEC_E_INCOMPLETE_MESSAGE))
251             ErrorExit("%s: Could not decrypt received message (DecryptMessage failed with error code 0x%lX)", ARGV0, status);
252
253         if (status == SEC_E_OK)
254         {
255             has_extra_data = 0;
256             for (i = 1; i < 4; ++i)
257                 if (msg_buffers[i].BufferType == SECBUFFER_EXTRA)
258                 {
259                     has_extra_data = 1;
260                     memcpy(buffer, msg_buffers[i].pvBuffer, msg_buffers[i].cbBuffer);
261                     buffer_length = msg_buffers[i].cbBuffer;
262                 }
263         }
264     }
265
266     for (i = 1; i < 4; ++i)
267         if (msg_buffers[i].BufferType == SECBUFFER_DATA)
268             return msg_buffers[i].pvBuffer;
269
270     return NULL;
271 }
272
273 void InstallAuthKeys(char *msg)
274 {
275     if (strncmp(msg, "ERROR", 5) == 0)
276         ErrorExit("%s: %s (from manager)", ARGV0, msg);
277     else if (strncmp(msg, "OSSEC K:'", 9) == 0)
278     {
279         char *key;
280         char *tmpstr;
281         char **entry;
282         FILE *fp;
283
284         printf("INFO: Received response with agent key\n");
285
286         key = msg + 9;
287         tmpstr = strchr(key, '\'');
288
289         if (!tmpstr)
290             ErrorExit("%s: Invalid key received. Closing connection.", ARGV0);
291
292         *tmpstr = '\0';
293         entry = OS_StrBreak(' ', key, 4);
294
295         if (!OS_IsValidID(entry[0]) || !OS_IsValidName(entry[1]) ||
296             !OS_IsValidName(entry[2]) || !OS_IsValidName(entry[3]))
297             ErrorExit("%s: Invalid key received (2). Closing connection.", ARGV0);
298
299         fp = fopen(KEYSFILE_PATH, "w");
300
301         if (!fp)
302             ErrorExit("%s: Unable to open key file: %s", ARGV0, KEYSFILE_PATH);
303
304         fprintf(fp, "%s\n", key);
305         fclose(fp);
306
307         printf("INFO: Valid key created. Finished.\n");
308     }
309     else
310         ErrorExit("%s: Unknown message received (%s)", ARGV0, msg);
311 }
312
313 void DisconnectFromServer(const int socket, CtxtHandle *context, CredHandle *cred)
314 {
315     SecBufferDesc OutBuffer;
316     SecBuffer OutBuffers[1];
317     DWORD dwType;
318     SECURITY_STATUS status;
319     DWORD input_flags;
320     DWORD output_flags;
321     int sent = 0;
322
323     dwType = SCHANNEL_SHUTDOWN;
324
325     OutBuffers[0].pvBuffer   = &dwType;
326     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
327     OutBuffers[0].cbBuffer   = sizeof(dwType);
328
329     OutBuffer.cBuffers  = 1;
330     OutBuffer.pBuffers  = OutBuffers;
331     OutBuffer.ulVersion = SECBUFFER_VERSION;
332
333     status = ApplyControlToken(context, &OutBuffer);
334     if (status != SEC_E_OK)
335         ErrorExit("%s: Could not correclty close connection", ARGV0);
336
337     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;
338     OutBuffers[0].pvBuffer   = NULL;
339     OutBuffers[0].BufferType = SECBUFFER_TOKEN;
340     OutBuffers[0].cbBuffer   = 0;
341
342     OutBuffer.cBuffers  = 1;
343     OutBuffer.pBuffers  = OutBuffers;
344     OutBuffer.ulVersion = SECBUFFER_VERSION;
345
346     status = InitializeSecurityContext(cred, context, NULL, input_flags, 0, 0, NULL, 0, context, &OutBuffer, &output_flags, NULL);
347     if (status != SEC_E_OK)
348         ErrorExit("%s: Could not correclty close connection (2)", ARGV0);
349
350     sent = send(socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
351     if (sent <= 0)
352         ErrorExit("%s: Could not correclty close connection (3)", ARGV0);
353
354     FreeContextBuffer(OutBuffers[0].pvBuffer);
355     DeleteSecurityContext(context);
356     close(socket);
357 }
358
359 int main(int argc, char **argv)
360 {
361     int error = 0;
362     int socket = 0;
363     char *port = "1515";
364     char c = 0;
365     char *manager = NULL;
366     char *agentname = NULL;
367     char hostname[512];
368     char *msg = NULL;
369     char *authpass = NULL;
370     char buf[4096 + 1] = { '\0' };
371     WSADATA wsa;
372     CtxtHandle context;
373     CredHandle cred;
374
375     /* Setting the name */
376     OS_SetName(ARGV0);
377
378     while((c = getopt(argc, argv, "hm:p:A:P:")) != -1)
379     {
380         switch(c){
381             case 'h':
382                 report_help();
383                 break;
384             case 'm':
385                if(!optarg)
386                     ErrorExit("%s: -%c needs an argument",ARGV0, c);
387                 manager = optarg;
388                 break;
389             case 'A':
390                if(!optarg)
391                     ErrorExit("%s: -%c needs an argument",ARGV0, c);
392                 agentname = optarg;
393                 break;
394             case 'p': {
395                if(!optarg)
396                     ErrorExit("%s: -%c needs an argument",ARGV0, c);
397                 int tmp_port;
398                 tmp_port = atoi(optarg);
399                 if(tmp_port <= 0 || tmp_port >= 65536)
400                 {
401                     ErrorExit("%s: Invalid port: %s", ARGV0, optarg);
402                 }
403                 port = optarg;
404                 break;
405             }
406             case 'P':
407                 if (!optarg)
408                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
409
410                 authpass = optarg;
411                 break;
412             default:
413                 report_help();
414                 break;
415         }
416     }
417
418     // Initialize Windows Networking
419     error = WSAStartup(MAKEWORD(2, 2), &wsa);
420     if (error)
421         ErrorExit("%s: Could not initialize networking (WSAStartup failed with error code %u)", ARGV0, error);
422
423     // Determine agent_name
424     if(agentname == NULL)
425     {
426         if(gethostname(hostname, 512) != 0)
427             ErrorExit("%s: ERROR: Unable to extract hostname. Custom agent name not set.", ARGV0);
428
429         agentname = hostname;
430     }
431
432     /* Checking if there is a custom password file */
433     if (authpass == NULL) {
434         FILE *fp;
435         fp = fopen(AUTHDPASS_PATH, "r");
436         buf[0] = '\0';
437
438         if (fp) {
439             buf[4096] = '\0';
440             char *ret = fgets(buf, 4095, fp);
441
442             if (ret && strlen(buf) > 2) {
443                 authpass = buf;
444             }
445
446             fclose(fp);
447             printf("INFO: Using password specified on file: %s\n", AUTHDPASS_PATH);
448         }
449     }
450     if (!authpass) {
451         printf("WARN: No authentication password provided. Insecure mode started.\n");
452
453     }
454
455     // Connect to socket and init security context
456     CreateSecureConnection(manager, port, &socket, &context, &cred);
457     
458     printf("INFO: Using agent name as: %s\n", agentname);
459
460     // Send request
461
462     if (authpass)
463         SendSecureMessage(socket, &context, "OSSEC PASS: %s OSSEC A:'%s'\n", authpass, agentname);
464     else
465         SendSecureMessage(socket, &context, "OSSEC A:'%s'\n", agentname);
466
467     printf("INFO: Sent request to manager. Waiting for reply.\n");
468
469     // Get response
470     msg = ReceiveSecureMessage(socket, &context);
471
472     // Install received keys
473     InstallAuthKeys(msg);
474
475     // Disconnect
476     DisconnectFromServer(socket, &context, &cred);
477
478     return (0);
479 }