source: trunk/src/yulectl.c@ 575

Last change on this file since 575 was 562, checked in by katerina, 3 years ago

Fix for ticket #450 (compiler warnings) and fixes for tests.

File size: 15.0 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2003 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#include <stddef.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <ctype.h>
27#include <errno.h>
28
29#include <unistd.h>
30#include <fcntl.h>
31
32#include <sys/socket.h>
33#include <sys/un.h>
34
35#include <signal.h>
36#include <pwd.h>
37
38#if !defined(AF_FILE)
39#define AF_FILE AF_UNIX
40#endif
41
42#define SH_MAXMSG 209
43
44#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
45 !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && \
46 !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
47#define SH_REQ_PASSWORD 1
48#endif
49
50#define SH_PW_SIZE 15
51
52static int sock = -1;
53static char password[SH_PW_SIZE] = "";
54static int verbose = 0;
55
56#ifdef SH_STEALTH
57char * globber(const char * string);
58#define _(string) globber(string)
59#define N_(string) string
60#else
61#define _(string) string
62#define N_(string) string
63#endif
64
65#ifdef SH_STEALTH
66#ifndef SH_MAX_GLOBS
67#define SH_MAX_GLOBS 32
68#endif
69char * globber(const char * str)
70{
71 register int i, j;
72 static int count = -1;
73 static char glob[SH_MAX_GLOBS][128];
74
75 ++count; if (count > (SH_MAX_GLOBS-1) ) count = 0;
76 j = strlen(str);
77 if (j > 127) j = 127;
78
79 for (i = 0; i < j; ++i)
80 {
81 if (str[i] != '\n' && str[i] != '\t')
82 glob[count][i] = str[i] ^ XOR_CODE;
83 else
84 glob[count][i] = str[i];
85 }
86 glob[count][j] = '\0';
87 return glob[count];
88}
89#endif
90
91#define CLIENT _("yulectl")
92
93
94static int
95create_unix_socket ()
96{
97 int sock;
98
99 /* Create the socket. */
100
101 sock = socket (PF_UNIX, SOCK_STREAM, 0);
102 if (sock < 0)
103 {
104 perror (_("ERROR: socket"));
105 return -1;
106 }
107
108 return sock;
109}
110
111static void
112termination_handler (int signum)
113{
114 /* Clean up. */
115 if (signum != 0)
116 {
117 if (verbose)
118 fprintf(stdout, _("# Terminated on signal %d\n"), signum);
119 }
120 if (sock >= 0 )
121 close (sock);
122 return;
123}
124
125static char * safe_copy(char * to, const char * from, size_t size)
126{
127 if (to && from && (size > 0))
128 {
129 strncpy (to, from, size-1);
130 to[size-1] = '\0';
131 }
132 return to;
133}
134
135
136static int send_to_server (char * serversock, char * message)
137{
138 struct sockaddr_un name;
139 int size;
140 int nbytes;
141
142 /* Initialize the server socket address.
143 */
144 name.sun_family = AF_UNIX;
145 memcpy(name.sun_path, serversock, sizeof(name.sun_path));
146 name.sun_path[sizeof(name.sun_path)-1] = '\0';
147 if (strlen(serversock) > strlen(name.sun_path))
148 {
149 perror (_("ERROR: socket path too long"));
150 return -1;
151 }
152 size = (offsetof (struct sockaddr_un, sun_path)
153 + strlen (name.sun_path) + 1);
154
155 nbytes = connect(sock, (struct sockaddr *) & name, size);
156 if (nbytes < 0)
157 {
158 perror (_("ERROR: connect"));
159 return -1;
160 }
161
162 /* Send the data.
163 */
164 nbytes = send (sock, message, strlen (message) + 1, 0);
165 if (nbytes < 0)
166 {
167 perror (_("ERROR: send"));
168 return -1;
169 }
170 return 0;
171}
172
173static int getline_from_server (int sock, char * buf, int size)
174{
175 int nbytes = 0;
176 int status = 0;
177 char * p = buf;
178
179 do {
180 status = read (sock, p, 1);
181 if (status <= 0)
182 {
183 buf[nbytes] = '\0';
184 return ((status == 0) ? nbytes : status);
185 }
186 else if (*p == '\0')
187 {
188 return nbytes;
189 }
190 ++nbytes; ++p;
191 } while (nbytes < size);
192 buf[size-1] = '\0';
193 return 0;
194}
195
196static int recv_from_server (char * message)
197{
198 int nbytes = 0;
199 char recvmsg[SH_MAXMSG];
200 int num = 0;
201 int good = -1;
202 char * p;
203
204 if (password[0] == '\0')
205 p = message;
206 else
207 p = &message[strlen(password)+1];
208
209 if (0 == strncmp(p, _("PROBE"), 5) ||
210 0 == strncmp(p, _("LIST"), 4))
211 {
212 do {
213 nbytes = getline_from_server (sock, recvmsg, SH_MAXMSG);
214 if (nbytes < 0)
215 {
216 if (errno == EAGAIN)
217 return 0;
218 else
219 {
220 perror (_("ERROR: recv"));
221 return -1;
222 }
223 }
224 else if (nbytes == 0)
225 return 0;
226
227 if (recvmsg[0] == 'E' && recvmsg[1] == 'N' && recvmsg[2] == 'D')
228 {
229 if (verbose && (num == 0))
230 fprintf (stdout, "%s", _("# There are no pending commands.\n"));
231 return 0;
232 }
233 ++num;
234 fprintf (stdout, _("%03d: %s\n"), num, recvmsg);
235 } while (nbytes >= 0);
236 }
237 else
238 {
239 nbytes = recv (sock, recvmsg, SH_MAXMSG, 0);
240 if (nbytes < 0)
241 {
242 perror (_("ERROR: recv"));
243 return -1;
244 }
245 }
246
247 /* Print a diagnostic message. */
248 if (password[0] == '\0')
249 good = strcmp (message, recvmsg);
250 else
251 good = strcmp (&message[strlen(password)+1], recvmsg);
252
253 if (0 != good)
254 {
255 if (0 == strncmp(recvmsg, _("!E:"), 3))
256 {
257 fputs(recvmsg, stderr);
258 fputc('\n', stderr);
259 }
260 else
261 {
262 fputs (_("ERROR: Bounced message != original message.\n"), stderr);
263 }
264 return -1;
265 }
266 else
267 {
268 if (verbose)
269 fprintf (stdout, "%s", _("# Message received by server.\n"));
270 }
271
272 return 0;
273}
274
275static int check_uuid(const char * in)
276{
277 int i;
278 const char *cp;
279
280 if (!in || strlen(in) != 36)
281 return -1;
282 for (i=0, cp = in; i <= 36; i++,cp++) {
283 if ((i == 8) || (i == 13) || (i == 18) ||
284 (i == 23)) {
285 if (*cp == '-')
286 continue;
287 else
288 return -1;
289 }
290 if (i== 36)
291 if (*cp == 0)
292 continue;
293 if (!isxdigit(*cp))
294 return -1;
295 }
296 return 0;
297}
298
299static int check_command(const char * str)
300{
301 unsigned int i = 0;
302 char * commands[] = { N_("DELTA:"), N_("RELOAD"), N_("STOP"), N_("SCAN"),
303 N_("CANCEL"), N_("LISTALL"), N_("LIST"), N_("PROBE"), NULL };
304
305 while (commands[i])
306 {
307 size_t len = strlen(_(commands[i]));
308
309 if (0 == strncmp(_(commands[i]), str, len))
310 {
311 if (i == 0)
312 {
313 char * p = strchr(str, ':'); ++p;
314 if ( 0 == check_uuid(p) )
315 return 0;
316 }
317 else
318 {
319 if (len == strlen(str))
320 return 0;
321 }
322 }
323 ++i;
324 }
325
326 fprintf (stderr, _("ERROR: invalid command <%s>\n\n"), str);
327 return -1;
328}
329
330static void print_usage_and_exit(char * name, int exit_status)
331{
332 printf(_("\nUsage : %s [-v][-s server_socket] -c command <client_hostname>\n\n"),
333 name);
334
335 printf("%s", _("Purpose : send commands to the server via a socket,\n"));
336 printf("%s", _(" in particular commands that the server would\n"));
337 printf("%s", _(" transfer to the client <client_hostname> when\n"));
338 printf("%s", _(" this client connects to deliver a message.\n\n"));
339 printf("%s", _(" If password is required, it is read from\n"));
340 printf("%s", _(" $HOME/.yulectl_cred or taken from the environment\n"));
341 printf("%s", _(" variable YULECTL_PASSWORD (not recommended).\n\n"));
342
343 printf("%s", _("Commands: RELOAD reload configuration\n"));
344 printf("%s", _(" DELTA:<uuid> load delta database with given uuid\n"));
345 printf("%s", _(" STOP terminate\n"));
346 printf("%s", _(" SCAN initiate file system check\n"));
347 printf("%s", _(" CANCEL cancel pending command(s)\n"));
348 printf("%s", _(" LIST list queued commands\n"));
349 printf("%s", _(" LISTALL list queued and last sent commands\n"));
350 printf("%s", _(" PROBE probe all clients for necessity of reload\n"));
351 exit(exit_status);
352}
353
354char * rtrim(char * str)
355{
356 size_t len;
357
358 if (!str) return str;
359
360 len = strlen(str);
361 while (len > 0)
362 {
363 --len;
364 if (str[len] == '\n' || str[len] == '\r')
365 str[len] = '\0';
366 else
367 break;
368 }
369 return str;
370}
371
372static int get_home(char * home, size_t size)
373{
374 struct passwd * pwent;
375
376 pwent = getpwuid(geteuid());
377 if ((pwent == 0) || (pwent->pw_dir == NULL))
378 {
379 if (verbose)
380 fprintf (stderr, _("WARNING: no home directory for euid %ld\n"),
381 (long) geteuid());
382 if (NULL != getenv(_("HOME")))
383 {
384 safe_copy(home, getenv(_("HOME")), size);
385 }
386 else
387 {
388 fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid());
389 return -1;
390 }
391 }
392 else
393 {
394 safe_copy(home, pwent->pw_dir, size);
395 }
396 return 0;
397}
398
399static int get_passwd(char * message2, size_t size)
400{
401 char home[4096];
402 FILE * fp;
403 char * pw;
404
405 /* 1) Password from environment
406 */
407 pw = getenv(_("YULECTL_PASSWORD"));
408 if (pw && strlen(pw) < SH_PW_SIZE)
409 {
410 strcpy(password, pw);
411 strcpy(message2, password);
412 return 0;
413 }
414
415 /* 2) Password from $HOME/.yule_cred
416 */
417 if (get_home(home, sizeof(home)) < 0)
418 return -1;
419
420 if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > sizeof(home))
421 {
422 fprintf (stderr, "%s", _("ERROR: path for $HOME is too long.\n"));
423 return -1;
424 }
425 strcat(home, _("/.yulectl_cred"));
426 fp = fopen(home, "r");
427
428#if defined(SH_REQ_PASSWORD)
429 if (fp == NULL)
430 {
431 if (errno == ENOENT) {
432 fprintf (stderr,
433 _("ERROR No password file (%s) exists\n"),
434 home);
435 }
436 else {
437 fprintf (stderr,
438 _("ERROR: Password file (%s) not accessible for euid %ld uid %ld\n"),
439 home, (long)geteuid(), (long)getuid());
440 }
441 return -1;
442 }
443#else
444 if (fp == NULL)
445 return 0;
446#endif
447
448 if (NULL == fgets(message2, size, fp))
449 {
450 fprintf (stderr,
451 _("ERROR: empty or unreadable password file (%s).\n"),
452 home);
453 return -1;
454 }
455
456 (void) rtrim(message2);
457
458 if (strlen(message2) > (SH_PW_SIZE -1))
459 {
460 fprintf (stderr, "%s",
461 _("ERROR: Password too long (max. 14 characters).\n"));
462 return -1;
463 }
464 strcpy(password, message2);
465 fclose(fp);
466
467 return 0;
468}
469
470static int fixup_message (char * message)
471{
472 char message_fixed[SH_MAXMSG] = { 0 };
473
474 if (get_passwd(message_fixed, sizeof(message_fixed)) < 0)
475 return -1;
476
477 if (strlen(message_fixed) > 0)
478 {
479 strcat(message_fixed, "@");
480
481 strncat(message_fixed, message, SH_MAXMSG - strlen(message_fixed) -1);
482 message_fixed[SH_MAXMSG-1] = '\0';
483 strcpy(message, message_fixed);
484 }
485 return 0;
486}
487
488static int fill_serversock(char * serversock, size_t size)
489{
490 int status;
491
492#ifdef HAVE_VSNPRINTF
493 status = snprintf(serversock, size, _("%s/%s.sock"),
494 DEFAULT_PIDDIR, SH_INSTALL_NAME);
495#else
496 if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > size)
497 status = -1;
498 else
499 status = sprintf (serversock, _("%s/%s.sock"),
500 DEFAULT_PIDDIR, SH_INSTALL_NAME);
501#endif
502
503 if ((status < 0) || (status > (int)(size-1)))
504 {
505 fprintf(stderr, _("ERROR: Path too long (maximum %d): %s/%s.sock\n"),
506 (int) (size-1), DEFAULT_PIDDIR, SH_INSTALL_NAME);
507 return -1;
508 }
509 return 0;
510}
511
512static void checklen(char * command, char * str, size_t maxlen)
513{
514 if (strlen(str) > maxlen)
515 {
516 fprintf(stderr, _("ERROR: String too long (max %d): %s\n\n"),
517 (int) maxlen, str);
518 print_usage_and_exit (command, EXIT_FAILURE);
519 }
520 return;
521}
522
523static void checknull(char * command, char * str)
524{
525 if (str == NULL || str[0] == '\0') {
526 fprintf(stderr, "%s", _("ERROR: option with missing argument\n\n"));
527 print_usage_and_exit(command, EXIT_FAILURE);
528 }
529 return;
530}
531
532int
533main (int argc, char * argv[])
534{
535
536 char message[SH_MAXMSG] = "";
537 char serversock[256];
538 int status;
539 int num = 1;
540 int flag = 0;
541
542 if (fill_serversock(serversock, sizeof(serversock)) < 0)
543 return (EXIT_FAILURE);
544
545
546 while (argc > 1 && argv[num][0] == '-')
547 {
548 switch (argv[num][1])
549 {
550 case 'h':
551 print_usage_and_exit(argv[0], EXIT_SUCCESS);
552 break;
553 case 'v':
554 ++verbose;
555 break;
556 case 's':
557 --argc; ++num;
558 checknull(argv[0], argv[num]);
559 checklen(argv[0], argv[num], sizeof(serversock)-1);
560 safe_copy (serversock, argv[num], sizeof(serversock));
561 break;
562 case 'c':
563 --argc; ++num;
564 checknull(argv[0], argv[num]);
565 checklen(argv[0], argv[num], SH_MAXMSG-1);
566 if (0 != check_command(argv[num]))
567 print_usage_and_exit(argv[0], EXIT_FAILURE);
568 safe_copy(message, argv[num], SH_MAXMSG);
569 strncat(message, ":", SH_MAXMSG-strlen(message)-1);
570 message[SH_MAXMSG-1] = '\0';
571 flag = 1;
572 break;
573 default:
574 fprintf(stderr, _("ERROR: unknown option -%c\n\n"), argv[num][1]);
575 print_usage_and_exit(argv[0], EXIT_FAILURE);
576 break;
577 }
578 --argc; ++num;
579 }
580
581 if (flag == 0) /* no command given */
582 print_usage_and_exit(argv[0], EXIT_FAILURE);
583
584 if (argc > 1)
585 {
586 checklen(argv[0], argv[num], SH_MAXMSG - strlen(message) - 1);
587 strncat (message, argv[num], SH_MAXMSG - strlen(message) - 1);
588 message[SH_MAXMSG-1] = '\0';
589 }
590 else
591 {
592 if (0 == strncmp(message, _("PROBE"), 5) ||
593 0 == strncmp(message, _("LIST"), 4))
594 {
595 strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
596 message[SH_MAXMSG-1] = '\0';
597 }
598 else
599 {
600 fprintf(stderr, "%s", _("ERROR: this command requires a hostname\n"));
601 print_usage_and_exit(argv[0], EXIT_FAILURE);
602 }
603 }
604
605 if (fixup_message(message) < 0)
606 return (EXIT_FAILURE);
607
608 /* Make the socket.
609 */
610 sock = create_unix_socket ();
611 if (sock < 0)
612 return (EXIT_FAILURE);
613
614 /* Set up termination handler.
615 */
616 signal (SIGINT, termination_handler);
617 signal (SIGHUP, termination_handler);
618 signal (SIGTERM, termination_handler);
619 signal (SIGQUIT, termination_handler);
620
621 /* Send the datagram.
622 */
623 status = send_to_server (serversock, message);
624 if (status < 0)
625 {
626 fprintf(stderr, "%s", _("ERROR: sending command to server failed\n"));
627 (void) termination_handler(0);
628 return (EXIT_FAILURE);
629 }
630
631 /* Wait for a reply.
632 */
633 if (verbose)
634 {
635 if (0 == strncmp(message, "LIST", 4))
636 fprintf(stdout, "%s", _("# Waiting for listing.\n"));
637 else
638 fprintf(stdout, "%s", _("# Waiting for confirmation.\n"));
639 }
640
641 status = recv_from_server (message);
642
643 if (status < 0)
644 {
645 fputs(_("ERROR: unexpected or no reply from server.\n"), stderr);
646 (void) termination_handler(0);
647 return (EXIT_FAILURE);
648 }
649
650 /* Clean up. */
651 (void) termination_handler(0);
652 return (EXIT_SUCCESS);
653}
654
Note: See TracBrowser for help on using the repository browser.