source: trunk/src/yulectl.c@ 451

Last change on this file since 451 was 396, checked in by katerina, 12 years ago

Fix for ticket #294 (minor issues with stealth mode).

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