source: trunk/src/yulectl.c @ 562

Last change on this file since 562 was 562, checked in by katerina, 5 months 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.