new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_auth / main-client.c
1 /* Copyright (C) 2010 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  *
9  * In addition, as a special exception, the copyright holders give
10  * permission to link the code of portions of this program with the
11  * OpenSSL library under certain conditions as described in each
12  * individual source file, and distribute linked combinations
13  * including the two.
14  *
15  * You must obey the GNU General Public License in all respects
16  * for all of the code used other than OpenSSL.  If you modify
17  * file(s) with this exception, you may extend this exception to your
18  * version of the file(s), but you are not obligated to do so.  If you
19  * do not wish to do so, delete this exception statement from your
20  * version.  If you delete this exception statement from all source
21  * files in the program, then also delete it here.
22  *
23  */
24
25 #include <errno.h>
26 #include <string.h>
27 #include "shared.h"
28 #include "check_cert.h"
29
30 #ifndef LIBOPENSSL_ENABLED
31
32 int main()
33 {
34     printf("ERROR: Not compiled. Missing OpenSSL support.\n");
35     exit(0);
36 }
37
38 #else
39
40 #include <openssl/ssl.h>
41 #include "auth.h"
42
43 static void help_agent_auth(void) __attribute__((noreturn));
44
45 /* Print help statement */
46 static void help_agent_auth()
47 {
48     print_header();
49     print_out("  %s: -[Vhdt] [-g group] [-D dir] [-m IP address] [-p port] [-A name] [-c ciphers] [-v path] [-x path] [-k path]", ARGV0);
50     print_out("    -V          Version and license message");
51     print_out("    -h          This help message");
52     print_out("    -d          Execute in debug mode. This parameter");
53     print_out("                can be specified multiple times");
54     print_out("                to increase the debug level.");
55     print_out("    -t          Test configuration");
56     print_out("    -g <group>  Group to run as (default: %s)", GROUPGLOBAL);
57     print_out("    -D <dir>    Directory to chroot into (default: %s)", DEFAULTDIR);
58     print_out("    -m <addr>   Manager IP address");
59     print_out("    -p <port>   Manager port (default: %s)", DEFAULT_PORT);
60     print_out("    -A <name>   Agent name (default: hostname)");
61     print_out("    -c          SSL cipher list (default: %s)", DEFAULT_CIPHERS);
62     print_out("    -v <path>   Full path to CA certificate used to verify the server");
63     print_out("    -x <path>   Full path to agent certificate");
64     print_out("    -k <path>   Full path to agent key");
65     print_out("    -P <path>   Authorization password file [default: /var/ossec/etc/authd.pass");
66     print_out(" ");
67     exit(1);
68 }
69
70 int main(int argc, char **argv)
71 {
72     int key_added = 0;
73     int c;
74     int test_config = 0;
75     int authenticate = 0;
76 #ifndef WIN32
77     gid_t gid = 0;
78 #endif
79
80     int sock = 0, portnum, ret = 0;
81     char *port = DEFAULT_PORT;
82     char *ciphers = DEFAULT_CIPHERS;
83     const char *dir = DEFAULTDIR;
84     const char *group = GROUPGLOBAL;
85     char *authpass = NULL;
86     const char *manager = NULL;
87     const char *agentname = NULL;
88     const char *agent_cert = NULL;
89     const char *agent_key = NULL;
90     const char *ca_cert = NULL;
91     char lhostname[512 + 1];
92     char buf[4096 + 1];
93     SSL_CTX *ctx;
94     SSL *ssl;
95     BIO *sbio;
96     bio_err = 0;
97     buf[4096] = '\0';
98
99 #ifdef WIN32
100     WSADATA wsaData;
101 #endif
102
103     /* Set the name */
104     OS_SetName(ARGV0);
105
106     while ((c = getopt(argc, argv, "Vdhtg:m:p:A:c:v:x:k:D:P:")) != -1) {
107         switch (c) {
108             case 'V':
109                 print_version();
110                 break;
111             case 'h':
112                 help_agent_auth();
113                 break;
114             case 'd':
115                 nowDebug();
116                 break;
117             case 'g':
118                 if (!optarg) {
119                     ErrorExit("%s: -g needs an argument", ARGV0);
120                 }
121                 group = optarg;
122                 break;
123             case 'D':
124             if (!optarg) {
125                 ErrorExit("%s: -g needs an argument", ARGV0);
126             }
127             dir = optarg;
128             break;
129             case 't':
130                 test_config = 1;
131                 break;
132             case 'm':
133                 if (!optarg) {
134                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
135                 }
136                 manager = optarg;
137                 break;
138             case 'A':
139                 if (!optarg) {
140                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
141                 }
142                 agentname = optarg;
143                 break;
144             case 'p':
145                 if (!optarg) {
146                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
147                 }
148                 portnum = atoi(optarg);
149                 if (portnum <= 0 || portnum >= 65536) {
150                     ErrorExit("%s: Invalid port: %s", ARGV0, optarg);
151                 }
152                 port = optarg;
153                 break;
154             case 'c':
155                 if (!optarg) {
156                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
157                 }
158                 ciphers = optarg;
159                 break;
160             case 'v':
161                 if (!optarg) {
162                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
163                 }
164                 ca_cert = optarg;
165                 break;
166             case 'x':
167                 if (!optarg) {
168                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
169                 }
170                 agent_cert = optarg;
171                 break;
172             case 'k':
173                 if (!optarg) {
174                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
175                 }
176                 agent_key = optarg;
177                 break;
178             case 'P':
179                 if (!optarg) {
180                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
181                 }
182                 authpass = optarg;
183                 authenticate++;
184                 break;
185             default:
186                 help_agent_auth();
187                 break;
188         }
189     }
190
191     /* Start daemon */
192     debug1(STARTED_MSG, ARGV0);
193
194 #ifndef WIN32
195     /* Check if the user/group given are valid */
196     gid = Privsep_GetGroup(group);
197     if (gid == (gid_t) - 1) {
198         ErrorExit(USER_ERROR, ARGV0, "", group);
199     }
200
201     /* Exit here if test config is set */
202     if (test_config) {
203         exit(0);
204     }
205
206     /* Privilege separation */
207     if (Privsep_SetGroup(gid) < 0) {
208         ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
209     }
210
211     /* Signal manipulation */
212     StartSIG(ARGV0);
213
214     /* Create PID files */
215     if (CreatePID(ARGV0, getpid()) < 0) {
216         ErrorExit(PID_ERROR, ARGV0);
217     }
218 #else
219     /* Initialize Windows socket stuff */
220     if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
221         ErrorExit("%s: WSAStartup() failed", ARGV0);
222     }
223 #endif /* WIN32 */
224
225     /* Start up message */
226     verbose(STARTUP_MSG, ARGV0, (int)getpid());
227
228     if (agentname == NULL) {
229         lhostname[512] = '\0';
230         if (gethostname(lhostname, 512 - 1) != 0) {
231             merror("%s: ERROR: Unable to extract hostname. Custom agent name not set.", ARGV0);
232             exit(1);
233         }
234         agentname = lhostname;
235     }
236
237     /* Start SSL */
238     ctx = os_ssl_keys(0, dir, ciphers, agent_cert, agent_key, ca_cert);
239     if (!ctx) {
240         merror("%s: ERROR: SSL error. Exiting.", ARGV0);
241         exit(1);
242     }
243
244     if (!manager) {
245         merror("%s: ERROR: Manager IP not set.", ARGV0);
246         exit(1);
247     }
248
249     /* Checking if there is a custom password file */
250     if (authpass != NULL && authenticate > 0) {
251         FILE *fp;
252         fp = fopen(authpass, "r");
253         if(!fp) {
254             fprintf(stderr, "Cannot open %s: %s\n", authpass, strerror(errno));
255             exit(1);
256         }
257         buf[0] = '\0';
258
259         if (fp) {
260             buf[4096] = '\0';
261             fgets(buf, 4095, fp);
262
263             if (strlen(buf) > 2) {
264                 authpass = strndup(buf, 32);
265                 if(!authpass) {
266                     fprintf(stderr, "Could not set the authpass: %s", strerror(errno));
267                     exit(1);
268                 }
269             }
270
271             fclose(fp);
272             printf("INFO: Using specified password.\n");
273         }
274     }
275     if (!authpass) {
276         printf("WARN: No authentication password provided. Insecure mode started.\n");
277     }
278
279     /* Connect via TCP */
280     sock = OS_ConnectTCP(port, manager);
281     if (sock <= 0) {
282         merror("%s: Unable to connect to %s:%s", ARGV0, manager, port);
283         exit(1);
284     }
285
286     /* Connect the SSL socket */
287     ssl = SSL_new(ctx);
288     sbio = BIO_new_socket(sock, BIO_NOCLOSE);
289     SSL_set_bio(ssl, sbio, sbio);
290
291     ret = SSL_connect(ssl);
292     if (ret <= 0) {
293         ERR_print_errors_fp(stderr);
294         merror("%s: ERROR: SSL error (%d). Exiting.", ARGV0, ret);
295         exit(1);
296     }
297
298     printf("INFO: Connected to %s:%s\n", manager, port);
299
300     /* Additional verification of the manager's certificate if a hostname
301      * rather than an IP address is given on the command line. Could change
302      * this to do the additional validation on IP addresses as well if needed.
303      */
304     if (ca_cert) {
305         printf("INFO: Verifying manager's certificate\n");
306         if (check_x509_cert(ssl, manager) != VERIFY_TRUE) {
307             debug1("%s: DEBUG: Unable to verify server certificate.", ARGV0);
308             exit(1);
309         }
310     }
311
312     printf("INFO: Using agent name as: %s\n", agentname);
313
314     memset(buf, 0, sizeof(buf));
315     if (authpass) {
316         snprintf(buf, 2048, "OSSEC PASS: %s OSSEC A:'%s'\n", authpass, agentname);
317     }
318     else {
319         snprintf(buf, 2048, "OSSEC A:'%s'\n", agentname);
320     }
321
322     ret = SSL_write(ssl, buf, strlen(buf));
323     if (ret < 0) {
324         printf("SSL write error (unable to send message.)\n");
325         ERR_print_errors_fp(stderr);
326         exit(1);
327     }
328
329     printf("INFO: Send request to manager. Waiting for reply.\n");
330
331     while (1) {
332         ret = SSL_read(ssl, buf, sizeof(buf) - 1);
333         switch (SSL_get_error(ssl, ret)) {
334             case SSL_ERROR_NONE:
335                 buf[ret] = '\0';
336                 if (strncmp(buf, "ERROR", 5) == 0) {
337                     char *tmpstr;
338                     tmpstr = strchr(buf, '\n');
339                     if (tmpstr) {
340                         *tmpstr = '\0';
341                     }
342                     printf("%s (from manager)\n", buf);
343                 } else if (strncmp(buf, "OSSEC K:'", 9) == 0) {
344                     char *key;
345                     char *tmpstr;
346                     char **entry;
347                     printf("INFO: Received response with agent key\n");
348
349                     key = buf;
350                     key += 9;
351                     tmpstr = strchr(key, '\'');
352                     if (!tmpstr) {
353                         printf("ERROR: Invalid key received. Closing connection.\n");
354                         exit(1);
355                     }
356                     *tmpstr = '\0';
357                     entry = OS_StrBreak(' ', key, 4);
358                     if (!OS_IsValidID(entry[0]) || !OS_IsValidName(entry[1]) ||
359                             !OS_IsValidName(entry[2]) || !OS_IsValidName(entry[3])) {
360                         printf("ERROR: Invalid key received (2). Closing connection.\n");
361                         exit(1);
362                     }
363
364                     {
365                         FILE *fp;
366                         fp = fopen(KEYSFILE_PATH, "w");
367                         if (!fp) {
368                             printf("ERROR: Unable to open key file: %s", KEYSFILE_PATH);
369                             exit(1);
370                         }
371                         fprintf(fp, "%s\n", key);
372                         fclose(fp);
373                     }
374                     key_added = 1;
375                     printf("INFO: Valid key created. Finished.\n");
376                 }
377                 break;
378             case SSL_ERROR_ZERO_RETURN:
379             case SSL_ERROR_SYSCALL:
380                 if (key_added == 0) {
381                     printf("ERROR: Unable to create key. Either wrong password or connection not accepted by the manager.\n");
382                 }
383                 printf("INFO: Connection closed.\n");
384                 exit(0);
385                 break;
386             default:
387                 printf("ERROR: SSL read (unable to receive message)\n");
388                 exit(1);
389                 break;
390         }
391
392     }
393
394     /* Shut down the socket */
395     if (key_added == 0) {
396         printf("ERROR: Unable to create key. Either wrong password or connection not accepted by the manager.\n");
397     }
398     SSL_CTX_free(ctx);
399     close(sock);
400
401     exit(0);
402 }
403
404 #endif /* LIBOPENSSL_ENABLED */