new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_auth / main-server.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 #ifndef LIBOPENSSL_ENABLED
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 int main()
30 {
31     printf("ERROR: Not compiled. Missing OpenSSL support.\n");
32     exit(0);
33 }
34
35 #else
36
37 #include <sys/wait.h>
38 #include "auth.h"
39 #include "os_crypto/md5/md5_op.h"
40
41 /* TODO: Pulled this value out of the sky, may or may not be sane */
42 #define POOL_SIZE 512
43
44 /* Prototypes */
45 static void help_authd(void) __attribute((noreturn));
46 static int ssl_error(const SSL *ssl, int ret);
47 static void clean_exit(SSL_CTX *ctx, int sock) __attribute__((noreturn));
48
49
50 /* Print help statement */
51 static void help_authd()
52 {
53     print_header();
54     print_out("  %s: -[Vhdti] [-g group] [-D dir] [-p port] [-c ciphers] [-v path] [-x path] [-k path]", ARGV0);
55     print_out("    -V          Version and license message");
56     print_out("    -h          This help message");
57     print_out("    -d          Execute in debug mode. This parameter");
58     print_out("                can be specified multiple times");
59     print_out("                to increase the debug level.");
60     print_out("    -t          Test configuration");
61     print_out("    -f          Run in foreground.");
62     print_out("    -i          Use client's source IP address");
63     print_out("    -g <group>  Group to run as (default: %s)", GROUPGLOBAL);
64     print_out("    -D <dir>    Directory to chroot into (default: %s)", DEFAULTDIR);
65     print_out("    -p <port>   Manager port (default: %s)", DEFAULT_PORT);
66     print_out("    -n          Disable shared password authentication (not recommended).\n");
67     print_out("    -c          SSL cipher list (default: %s)", DEFAULT_CIPHERS);
68     print_out("    -v <path>   Full path to CA certificate used to verify clients");
69     print_out("    -x <path>   Full path to server certificate");
70     print_out("    -k <path>   Full path to server key");
71     print_out(" ");
72     exit(1);
73 }
74
75 /* Generates a random and temporary shared pass to be used by the agents. */
76 char *__generatetmppass()
77 {
78     int rand1;
79     int rand2;
80     char *rand3;
81     char *rand4;
82     os_md5 md1;
83     os_md5 md3;
84     os_md5 md4;
85     char *fstring = NULL;
86     char str1[STR_SIZE +1];
87     char *muname = NULL;
88
89     #ifndef WIN32
90         #ifdef __OpenBSD__
91         srandomdev();
92         #else
93         srandom(time(0) + getpid() + getppid());
94         #endif
95     #else
96         srandom(time(0) + getpid());
97     #endif
98
99     rand1 = random();
100     rand2 = random();
101
102     rand3 = GetRandomNoise();
103     rand4 = GetRandomNoise();
104
105     OS_MD5_Str(rand3, md3);
106     OS_MD5_Str(rand4, md4);
107
108     muname = getuname();
109
110     snprintf(str1, STR_SIZE, "%d%d%s%d%s%s",(int)time(0), rand1, muname, rand2, md3, md4);
111     OS_MD5_Str(str1, md1);
112     fstring = strdup(md1);
113     free(rand3);
114     free(rand4);
115     if(muname) {
116         free(muname);
117     }
118     return(fstring);
119 }
120
121 /* Function to use with SSL on non blocking socket,
122  * to know if SSL operation failed for good
123  */
124 static int ssl_error(const SSL *ssl, int ret)
125 {
126     if (ret <= 0) {
127         switch (SSL_get_error(ssl, ret)) {
128             case SSL_ERROR_WANT_READ:
129             case SSL_ERROR_WANT_WRITE:
130                 usleep(100 * 1000);
131                 return (0);
132             default:
133                 merror("%s: ERROR: SSL Error (%d)", ARGV0, ret);
134                 ERR_print_errors_fp(stderr);
135                 return (1);
136         }
137     }
138
139     return (0);
140 }
141
142 static void clean_exit(SSL_CTX *ctx, int sock)
143 {
144     SSL_CTX_free(ctx);
145     close(sock);
146     exit(0);
147 }
148
149 /* Exit handler */
150 static void cleanup();
151
152
153
154 int main(int argc, char **argv)
155 {
156     FILE *fp;
157     char *authpass = NULL;
158     /* Bucket to keep pids in */
159     int process_pool[POOL_SIZE];
160     /* Count of pids we are wait()ing on */
161     int c = 0, test_config = 0, use_ip_address = 0, pid = 0, status, i = 0, active_processes = 0;
162     int use_pass = 1;
163     int run_foreground = 0;
164     gid_t gid;
165     int client_sock = 0, sock = 0, portnum, ret = 0;
166     char *port = DEFAULT_PORT;
167     char *ciphers = DEFAULT_CIPHERS;
168     const char *dir  = DEFAULTDIR;
169     const char *group = GROUPGLOBAL;
170     const char *server_cert = NULL;
171     const char *server_key = NULL;
172     const char *ca_cert = NULL;
173     char buf[4096 + 1];
174     SSL_CTX *ctx;
175     SSL *ssl;
176     char srcip[IPSIZE + 1];
177     struct sockaddr_storage _nc;
178     socklen_t _ncl;
179     fd_set fdsave, fdwork;              /* select() work areas */
180     int fdmax;                          /* max socket number + 1 */
181     OSNetInfo *netinfo;                 /* bound network sockets */
182     int esc = 0;                        /* while() escape flag */
183
184     /* Initialize some variables */
185     memset(srcip, '\0', IPSIZE + 1);
186     memset(process_pool, 0x0, POOL_SIZE * sizeof(*process_pool));
187     bio_err = 0;
188
189     OS_PassEmptyKeyfile();
190
191     /* Set the name */
192     OS_SetName(ARGV0);
193
194     while ((c = getopt(argc, argv, "Vdhtfig:D:m:p:c:v:x:k:n")) != -1) {
195         switch (c) {
196             case 'V':
197                 print_version();
198                 break;
199             case 'h':
200                 help_authd();
201                 break;
202             case 'd':
203                 nowDebug();
204                 break;
205             case 'i':
206                 use_ip_address = 1;
207                 break;
208             case 'g':
209                 if (!optarg) {
210                     ErrorExit("%s: -g needs an argument", ARGV0);
211                 }
212                 group = optarg;
213                 break;
214             case 'D':
215                 if (!optarg) {
216                     ErrorExit("%s: -D needs an argument", ARGV0);
217                 }
218                 dir = optarg;
219                 break;
220             case 't':
221                 test_config = 1;
222                 break;
223             case 'f':
224                 run_foreground = 1;
225                 break;
226             case 'n':
227                 use_pass = 0;
228                 break;
229             case 'p':
230                 if (!optarg) {
231                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
232                 }
233                 portnum = atoi(optarg);
234                 if (portnum <= 0 || portnum >= 65536) {
235                     ErrorExit("%s: Invalid port: %s", ARGV0, optarg);
236                 }
237                 port = optarg;
238                 break;
239             case 'c':
240                 if (!optarg) {
241                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
242                 }
243                 ciphers = optarg;
244                 break;
245             case 'v':
246                 if (!optarg) {
247                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
248                 }
249                 ca_cert = optarg;
250                 break;
251             case 'x':
252                 if (!optarg) {
253                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
254                 }
255                 server_cert = optarg;
256                 break;
257             case 'k':
258                 if (!optarg) {
259                     ErrorExit("%s: -%c needs an argument", ARGV0, c);
260                 }
261                 server_key = optarg;
262                 break;
263             default:
264                 help_authd();
265                 break;
266         }
267     }
268
269     /* Start daemon -- NB: need to double fork and setsid */
270     debug1(STARTED_MSG, ARGV0);
271
272     /* Check if the user/group given are valid */
273     gid = Privsep_GetGroup(group);
274     if (gid == (gid_t) - 1) {
275         ErrorExit(USER_ERROR, ARGV0, "", group);
276     }
277
278     if (!run_foreground) {
279         nowDaemon();
280         goDaemon();
281     }
282     
283     /* Create PID files */
284     if (CreatePID(ARGV0, getpid()) < 0) {
285         ErrorExit(PID_ERROR, ARGV0);
286     }
287
288     /* Exit here if test config is set */
289     if (test_config) {
290         exit(0);
291     }
292
293     /* Privilege separation */
294     if (Privsep_SetGroup(gid) < 0) {
295         ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
296     }
297
298     /* chroot -- TODO: this isn't a chroot. Should also close
299      * unneeded open file descriptors (like stdin/stdout)
300      */
301     if (chdir(dir) == -1) {
302         ErrorExit(CHDIR_ERROR, ARGV0, dir, errno, strerror(errno));
303     }
304
305     /* Signal manipulation */
306     StartSIG(ARGV0);
307
308
309     /* Create PID files */
310     if (CreatePID(ARGV0, getpid()) < 0) {
311         ErrorExit(PID_ERROR, ARGV0);
312     }
313
314     atexit(cleanup);
315
316     /* Start up message */
317     verbose(STARTUP_MSG, ARGV0, (int)getpid());
318
319     if (use_pass) {
320
321         /* Checking if there is a custom password file */
322         fp = fopen(AUTHDPASS_PATH, "r");
323         buf[0] = '\0';
324         if (fp) {
325             buf[4096] = '\0';
326             char *ret = fgets(buf, 4095, fp);
327
328             if (ret && strlen(buf) > 2) {
329                 /* Remove newline */
330                 buf[strlen(buf) - 1] = '\0';
331                 authpass = strdup(buf);
332             }
333
334             fclose(fp);
335         }
336
337         if (buf[0] != '\0')
338             verbose("Accepting connections. Using password specified on file: %s",AUTHDPASS_PATH);
339         else {
340             /* Getting temporary pass. */
341             authpass = __generatetmppass();
342             verbose("Accepting connections. Random password chosen for agent authentication: %s", authpass);
343         }
344     } else {
345         verbose("Accepting connections. No password required (not recommended)");
346     }
347
348     /* Getting SSL cert. */
349
350     fp = fopen(KEYSFILE_PATH, "a");
351     if (!fp) {
352         merror("%s: ERROR: Unable to open %s (key file)", ARGV0, KEYSFILE_PATH);
353         exit(1);
354     }
355     fclose(fp);
356
357     /* Start SSL */
358     ctx = os_ssl_keys(1, dir, ciphers, server_cert, server_key, ca_cert);
359     if (!ctx) {
360         merror("%s: ERROR: SSL error. Exiting.", ARGV0);
361         exit(1);
362     }
363
364     /* Connect via TCP */
365     netinfo = OS_Bindporttcp(port, NULL);
366     if (netinfo->status < 0) {
367         merror("%s: Unable to bind to port %s", ARGV0, port);
368         exit(1);
369     }
370
371     /* initialize select() save area */
372     fdsave = netinfo->fdset;
373     fdmax  = netinfo->fdmax;            /* value preset to max fd + 1 */
374
375     debug1("%s: DEBUG: Going into listening mode.", ARGV0);
376
377     /* Setup random */
378     srandom_init();
379
380     /* Chroot */
381 /*
382     if (Privsep_Chroot(dir) < 0)
383         ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
384
385     nowChroot();
386 */
387
388     while (1) {
389         /* No need to completely pin the cpu, 100ms should be fast enough */
390         usleep(100 * 1000);
391
392         /* Only check process-pool if we have active processes */
393         if (active_processes > 0) {
394             for (i = 0; i < POOL_SIZE; i++) {
395                 int rv = 0;
396                 status = 0;
397                 if (process_pool[i]) {
398                     rv = waitpid(process_pool[i], &status, WNOHANG);
399                     if (rv != 0) {
400                         debug1("%s: DEBUG: Process %d exited", ARGV0, process_pool[i]);
401                         process_pool[i] = 0;
402                         active_processes = active_processes - 1;
403                     }
404                 }
405             }
406         }
407         memset(&_nc, 0, sizeof(_nc));
408         _ncl = sizeof(_nc);
409
410         fdwork = fdsave;
411         if (select (fdmax, &fdwork, NULL, NULL, NULL) < 0) {
412             ErrorExit("ERROR: Call to os_auth select() failed, errno %d - %s",
413                       errno, strerror (errno));
414         }
415
416         /* read through socket list for active socket */
417         for (sock = 0; sock <= fdmax; sock++) {
418             if (FD_ISSET (sock, &fdwork)) {
419                 if ((client_sock = accept(sock, (struct sockaddr *) &_nc, &_ncl)) > 0) {
420                     if (active_processes >= POOL_SIZE) {
421                         merror("%s: Error: Max concurrency reached. Unable to fork", ARGV0);
422                         esc = 1; /* exit while(1) loop */
423                         break;
424                     }
425                     pid = fork();
426                     if (pid) {
427                         active_processes = active_processes + 1;
428                         close(client_sock);
429                         for (i = 0; i < POOL_SIZE; i++) {
430                             if (! process_pool[i]) {
431                                 process_pool[i] = pid;
432                                 break;
433                             }
434                         }
435                     } else {
436                         satop((struct sockaddr *) &_nc, srcip, IPSIZE);
437                         char *agentname = NULL;
438                         ssl = SSL_new(ctx);
439                         SSL_set_fd(ssl, client_sock);
440         
441                         do {
442                             ret = SSL_accept(ssl);
443         
444                             if (ssl_error(ssl, ret)) {
445                                 clean_exit(ctx, client_sock);
446                             }
447         
448                         } while (ret <= 0);
449                         verbose("%s: INFO: New connection from %s", ARGV0, srcip);
450                         buf[0] = '\0';
451         
452                         do {
453                             ret = SSL_read(ssl, buf, sizeof(buf));
454         
455                             if (ssl_error(ssl, ret)) {
456                                 clean_exit(ctx, client_sock);
457                             }
458         
459                         } while (ret <= 0);
460         
461                         int parseok = 0;
462                         char *tmpstr = buf;
463         
464                         /* Checking for shared password authentication. */
465                         if(authpass) {
466                             /* Format is pretty simple: OSSEC PASS: PASS WHATEVERACTION */
467                             if (strncmp(tmpstr, "OSSEC PASS: ", 12) == 0) {
468                                 tmpstr = tmpstr + 12;
469         
470                                 if (strlen(tmpstr) > strlen(authpass) && strncmp(tmpstr, authpass, strlen(authpass)) == 0) {
471                                     tmpstr += strlen(authpass);
472         
473                                     if (*tmpstr == ' ') {
474                                         tmpstr++;
475                                         parseok = 1;
476                                     }
477                                 }
478                             }
479                             if (parseok == 0) {
480                                 merror("%s: ERROR: Invalid password provided by %s. Closing connection.", ARGV0, srcip);
481                                 SSL_CTX_free(ctx);
482                                 close(client_sock);
483                                 exit(0);
484                             }
485                         }
486         
487                         /* Checking for action A (add agent) */
488                         parseok = 0;
489                         if (strncmp(tmpstr, "OSSEC A:'", 9) == 0) {
490                             agentname = tmpstr + 9;
491                             tmpstr += 9;
492                             while (*tmpstr != '\0') {
493                                 if (*tmpstr == '\'') {
494                                     *tmpstr = '\0';
495                                     verbose("%s: INFO: Received request for a new agent (%s) from: %s", ARGV0, agentname, srcip);
496                                     parseok = 1;
497                                     break;
498                                 }
499                                 tmpstr++;
500                             }
501                         }
502                         if (parseok == 0) {
503                             merror("%s: ERROR: Invalid request for new agent from: %s", ARGV0, srcip);
504                         } else {
505                             int acount = 2;
506                             char fname[2048 + 1];
507                             char response[2048 + 1];
508                             char *finalkey = NULL;
509                             response[2048] = '\0';
510                             fname[2048] = '\0';
511                             if (!OS_IsValidName(agentname)) {
512                                 merror("%s: ERROR: Invalid agent name: %s from %s", ARGV0, agentname, srcip);
513                                 snprintf(response, 2048, "ERROR: Invalid agent name: %s\n\n", agentname);
514                                 SSL_write(ssl, response, strlen(response));
515                                 snprintf(response, 2048, "ERROR: Unable to add agent.\n\n");
516                                 SSL_write(ssl, response, strlen(response));
517                                 sleep(1);
518                                 exit(0);
519                             }
520         
521                             /* Check for duplicate names */
522                             strncpy(fname, agentname, 2048);
523                             while (NameExist(fname)) {
524                                 snprintf(fname, 2048, "%s%d", agentname, acount);
525                                 acount++;
526                                 if (acount > 256) {
527                                     merror("%s: ERROR: Invalid agent name %s (duplicated)", ARGV0, agentname);
528                                     snprintf(response, 2048, "ERROR: Invalid agent name: %s\n\n", agentname);
529                                     SSL_write(ssl, response, strlen(response));
530                                     snprintf(response, 2048, "ERROR: Unable to add agent.\n\n");
531                                     SSL_write(ssl, response, strlen(response));
532                                     sleep(1);
533                                     exit(0);
534                                 }
535                             }
536                             agentname = fname;
537
538                             /* Check for duplicate IP addresses */
539                             char *check_ip_address = NULL;
540                             check_ip_address = IPExist(srcip);
541                             if(check_ip_address) {
542                                 merror("%s: ERROR: Invalid IP address %s (duplicated)", ARGV0, check_ip_address);
543                                 snprintf(response, 2048, "ERROR: Invalid IP address: %s\n\n", check_ip_address);
544                                 SSL_write(ssl, response, strlen(response));
545                                 snprintf(response, 2048, "ERROR: Unable to add agent.\n\n");
546                                 SSL_write(ssl, response, strlen(response));
547                                 sleep(1);
548                                 exit(0);
549                             }
550         
551                             /* Add the new agent */
552                             if (use_ip_address) {
553                                 finalkey = OS_AddNewAgent(agentname, srcip, NULL);
554                             } else {
555                                 finalkey = OS_AddNewAgent(agentname, NULL, NULL);
556                             }
557                             if (!finalkey) {
558                                 merror("%s: ERROR: Unable to add agent: %s (internal error)", ARGV0, agentname);
559                                 snprintf(response, 2048, "ERROR: Internal manager error adding agent: %s\n\n", agentname);
560                                 SSL_write(ssl, response, strlen(response));
561                                 snprintf(response, 2048, "ERROR: Unable to add agent.\n\n");
562                                 SSL_write(ssl, response, strlen(response));
563                                 sleep(1);
564                                 exit(0);
565                             }
566         
567                             snprintf(response, 2048, "OSSEC K:'%s'\n\n", finalkey);
568                             verbose("%s: INFO: Agent key generated for %s (requested by %s)", ARGV0, agentname, srcip);
569                             ret = SSL_write(ssl, response, strlen(response));
570                             if (ret < 0) {
571                                 merror("%s: ERROR: SSL write error (%d)", ARGV0, ret);
572                                 merror("%s: ERROR: Agen key not saved for %s", ARGV0, agentname);
573                                 ERR_print_errors_fp(stderr);
574                             } else {
575                                 verbose("%s: INFO: Agent key created for %s (requested by %s)", ARGV0, agentname, srcip);
576                             }
577                         }
578         
579                         clean_exit(ctx, client_sock);
580                     }
581                 }
582             } /* if active socket */
583         } /* for() loop on available sockets */
584
585         /* check for while() escape flag */
586         if (esc == 1)
587             break;
588
589     } /* while(1) loop for messages */
590
591     /* Shut down the socket */
592     clean_exit(ctx, sock);
593
594     return (0);
595 }
596
597 /* Exit handler */
598 static void cleanup() {
599         DeletePID(ARGV0);
600 }
601 #endif /* LIBOPENSSL_ENABLED */