source: trunk/src/yulectl.c @ 481

Last change on this file since 481 was 481, checked in by katerina, 6 years ago

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

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