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 static int    sock     = -1;
51 static char   password[15] = "";
52 static int    verbose = 0;
53 
54 #ifdef SH_STEALTH
55 char * 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
globber(const char * str)67 char * 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 
92 static int
create_unix_socket()93 create_unix_socket ()
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 
109 static void
termination_handler(int signum)110 termination_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     }
118   if (sock   >= 0 )
119     close  (sock);
120   return;
121 }
122 
safe_copy(char * to,const char * from,size_t size)123 static 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 
136 
send_to_server(char * serversock,char * message)137 static int send_to_server (char * serversock, char * message)
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 
157   /* Send the data.
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 
getline_from_server(int sock,char * buf,int size)168 static 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 
recv_from_server(char * message)191 static int recv_from_server (char * message)
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')
200     p = message;
201   else
202     p = &message[strlen(password)+1];
203 
204   if (0 == strncmp(p, _("PROBE"), 5) ||
205       0 == strncmp(p, _("LIST"),  4))
206     {
207       do {
208 	nbytes = getline_from_server (sock, recvmsg, SH_MAXMSG);
209 	if (nbytes < 0)
210 	  {
211 	    if (errno == EAGAIN)
212 	      return 0;
213 	    else
214 	      {
215 		perror (_("ERROR: recv"));
216 		return -1;
217 	      }
218 	  }
219 	else if (nbytes == 0)
220 	  return 0;
221 
222 	if (recvmsg[0] == 'E' && recvmsg[1] == 'N' && recvmsg[2] == 'D')
223 	  {
224 	    if (verbose && (num == 0))
225 	      fprintf (stdout, "%s", _("# There are no pending commands.\n"));
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')
244     good = strcmp (message, recvmsg);
245   else
246     good = strcmp (&message[strlen(password)+1], recvmsg);
247 
248   if (0 != good)
249     {
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 	}
259       return -1;
260     }
261   else
262     {
263       if (verbose)
264 	fprintf (stdout, "%s", _("# Message received by server.\n"));
265     }
266 
267   return 0;
268 }
269 
check_uuid(const char * in)270 static int check_uuid(const char * in)
271 {
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 
check_command(const char * str)294 static 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 
print_usage_and_exit(char * name,int exit_status)325 static void print_usage_and_exit(char * name, int exit_status)
326 {
327   printf(_("\nUsage : %s [-v][-s server_socket] -c command <client_hostname>\n\n"),
328 	 name);
329 
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"));
337 
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);
347 }
348 
rtrim(char * str)349 char * rtrim(char * str)
350 {
351   size_t len;
352 
353   if (!str) return str;
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 
get_home(char * home,size_t size)367 static int get_home(char * home, size_t size)
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 	{
379 	  safe_copy(home, getenv(_("HOME")), size);
380 	}
381       else
382 	{
383 	  fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid());
384 	  return -1;
385 	}
386     }
387   else
388     {
389       safe_copy(home, pwent->pw_dir, size);
390     }
391   return 0;
392 }
393 
get_passwd(char * message2,size_t size)394 static 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 
415   if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > 4096)
416     {
417       fprintf (stderr, "%s", _("ERROR: path for $HOME is too long.\n"));
418       return -1;
419     }
420   strcat(home, _("/.yulectl_cred"));
421   fp = fopen(home, "r");
422 
423 #if defined(SH_REQ_PASSWORD)
424   if (fp == NULL)
425     {
426       if (errno == ENOENT) {
427 	fprintf (stderr,
428 		 _("ERROR No password file (%s) exists\n"),
429 		 home);
430       }
431       else {
432 	fprintf (stderr,
433 		 _("ERROR: Password file (%s) not accessible for euid %ld uid %ld\n"),
434 		 home, (long)geteuid(), (long)getuid());
435       }
436       return -1;
437     }
438 #else
439   if (fp == NULL)
440     return 0;
441 #endif
442 
443   if (NULL == fgets(message2, size, fp))
444     {
445       fprintf (stderr,
446 	       _("ERROR: empty or unreadable password file (%s).\n"),
447 	       home);
448       return -1;
449     }
450 
451   (void) rtrim(message2);
452 
453   if (strlen(message2) > 14)
454     {
455       fprintf (stderr, "%s",
456 	       _("ERROR: Password too long (max. 14 characters).\n"));
457       return -1;
458     }
459   strcpy(password, message2);
460   fclose(fp);
461 
462   return 0;
463 }
464 
fixup_message(char * message)465 static 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;
481 }
482 
fill_serversock(char * serversock,size_t size)483 static int fill_serversock(char * serversock, size_t size)
484 {
485   int status;
486 
487 #ifdef HAVE_VSNPRINTF
488   status = snprintf(serversock, size, _("%s/%s.sock"),
489 		    DEFAULT_PIDDIR, SH_INSTALL_NAME);
490 #else
491   if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > size)
492     status = -1;
493   else
494     status = sprintf (serversock, _("%s/%s.sock"),
495 		      DEFAULT_PIDDIR, SH_INSTALL_NAME);
496 #endif
497 
498   if ((status < 0) || (status > (int)(size-1)))
499     {
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;
503     }
504   return 0;
505 }
506 
checklen(char * command,char * str,size_t maxlen)507 static void checklen(char * command, char * str, size_t maxlen)
508 {
509   if (strlen(str) > maxlen)
510     {
511       fprintf(stderr, _("ERROR: String too long (max %d): %s\n\n"),
512 	      (int) maxlen, str);
513       print_usage_and_exit (command, EXIT_FAILURE);
514     }
515   return;
516 }
517 
checknull(char * command,char * str)518 static 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 
527 int
main(int argc,char * argv[])528 main (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 
541   while (argc > 1 && argv[num][0] == '-')
542     {
543       switch (argv[num][1])
544 	{
545 	  case 'h':
546 	    print_usage_and_exit(argv[0], EXIT_SUCCESS);
547 	    break;
548 	  case 'v':
549 	    ++verbose;
550 	    break;
551 	  case 's':
552 	    --argc; ++num;
553 	    checknull(argv[0], argv[num]);
554 	    checklen(argv[0], argv[num], sizeof(serversock)-1);
555 	    safe_copy (serversock, argv[num], sizeof(serversock));
556 	    break;
557 	  case 'c':
558 	    --argc; ++num;
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;
567 	    break;
568 	  default:
569 	    fprintf(stderr, _("ERROR: unknown option -%c\n\n"), argv[num][1]);
570 	    print_usage_and_exit(argv[0], EXIT_FAILURE);
571 	    break;
572 	}
573       --argc; ++num;
574     }
575 
576   if (flag == 0) /* no command given */
577     print_usage_and_exit(argv[0], EXIT_FAILURE);
578 
579   if (argc > 1)
580     {
581       checklen(argv[0], argv[num], SH_MAXMSG - strlen(message) - 1);
582       strncat (message, argv[num], SH_MAXMSG - strlen(message) - 1);
583       message[SH_MAXMSG-1] = '\0';
584     }
585   else
586     {
587       if (0 == strncmp(message, _("PROBE"), 5) ||
588 	  0 == strncmp(message, _("LIST"),  4))
589 	{
590 	  strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
591 	  message[SH_MAXMSG-1] = '\0';
592 	}
593       else
594 	{
595 	  fprintf(stderr, "%s", _("ERROR: this command requires a hostname\n"));
596 	  print_usage_and_exit(argv[0], EXIT_FAILURE);
597 	}
598     }
599 
600   if (fixup_message(message) < 0)
601     return (EXIT_FAILURE);
602 
603   /* Make the socket.
604    */
605   sock = create_unix_socket ();
606   if (sock < 0)
607     return (EXIT_FAILURE);
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     {
621       fprintf(stderr, "%s", _("ERROR: sending command to server failed\n"));
622       (void) termination_handler(0);
623       return (EXIT_FAILURE);
624     }
625 
626   /* Wait for a reply.
627    */
628   if (verbose)
629     {
630       if (0 == strncmp(message, "LIST", 4))
631 	fprintf(stdout, "%s", _("# Waiting for listing.\n"));
632       else
633 	fprintf(stdout, "%s", _("# Waiting for confirmation.\n"));
634     }
635 
636   status = recv_from_server (message);
637 
638   if (status < 0)
639     {
640       fputs(_("ERROR: unexpected or no reply from server.\n"), stderr);
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 
650