source: trunk/src/yulectl.c @ 454

Last change on this file since 454 was 454, checked in by katerina, 7 years ago

Fix for ticket #355 (use calloc instead of malloc).

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 = calloc(1,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.