source: trunk/src/yulectl.c@ 96

Last change on this file since 96 was 34, checked in by rainer, 18 years ago

Code cleanup and minor fixes

File size: 14.4 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, _("# 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, _("ERROR: Bounced message != original message (possible reason: superfluous password).\n"));
303 return -1;
304 }
305 else
306 {
307 if (verbose)
308 fprintf (stdout, _("# 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(_("Purpose : send commands to the server via a socket,\n"));
320 printf(_(" in particular commands that the server would\n"));
321 printf(_(" transfer to the client <client_hostname> when\n"));
322 printf(_(" this client connects to deliver a message.\n\n"));
323 printf(_(" If password is required, it is read from\n"));
324 printf(_(" $HOME/.yulectl_cred or taken from the environment\n"));
325 printf(_(" variable YULECTL_PASSWORD (not recommended).\n\n"));
326
327 printf(_("Commands: RELOAD <reload configuration>\n"));
328 printf(_(" STOP <terminate>\n"));
329 printf(_(" CANCEL <cancel previous command>\n"));
330 printf(_(" LIST <list queued commands>\n"));
331 printf(_(" LISTALL <list queued and last sent commands>\n"));
332 printf(_(" PROBE <probe all clients for necessity of reload>\n"));
333 return;
334}
335
336void fixup_message (char * message)
337{
338 char message2[SH_MAXMSG];
339 char home[4096];
340 FILE * fp;
341 struct passwd * pwent;
342 char * pw;
343
344 pw = getenv(_("YULECTL_PASSWORD"));
345 if (pw && strlen(pw) < 15)
346 {
347 strcpy(password, pw);
348 strcpy(message2, password);
349 goto do_msg;
350 }
351
352 pwent = getpwuid(geteuid());
353 if ((pwent == 0) || (pwent->pw_dir == NULL))
354 {
355 if (verbose)
356 fprintf (stderr, _("WARNING: no home directory for euid %ld\n"),
357 (long) geteuid());
358 if (NULL != getenv(_("HOME")))
359 {
360 strncpy(home, getenv(_("HOME")), 4096);
361 home[4095] = '\0';
362 }
363 else
364 {
365 fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid());
366 exit(EXIT_FAILURE);
367 }
368 }
369 else
370 {
371 strncpy(home, pwent->pw_dir, 4096);
372 home[4095] = '\0';
373 }
374
375 if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > 4096)
376 {
377 fprintf (stderr, _("ERROR: path for $HOME is too long.\n"));
378 exit(EXIT_FAILURE);
379 }
380 strcat(home, _("/.yulectl_cred"));
381 fp = fopen(home, "r");
382 if (fp == NULL)
383 {
384 if (verbose && (errno == ENOENT))
385 fprintf (stdout,
386 _("# Password file (%s) missing\n"),
387 home);
388 else if (verbose)
389 fprintf (stdout,
390 _("# Password file (%s) not accessible for euid %ld uid %ld\n"),
391 home, (long)geteuid(), (long)getuid());
392 return;
393 }
394 if (NULL == fgets(message2, sizeof(message2), fp))
395 {
396 fprintf (stderr,
397 _("ERROR: empty or unreadable password file (%s).\n"),
398 home);
399 exit(EXIT_FAILURE);
400 }
401 if (strlen(message2) > 14)
402 {
403 fprintf (stderr,
404 _("ERROR: Password too long (max. 14 characters).\n"));
405 exit(EXIT_FAILURE);
406 }
407
408 if ( (0 != strlen(message2)) && ('\n' == message2[strlen(message2)-1]))
409 {
410 message2[strlen(message2)-1] = '\0';
411 }
412 strcpy(password, message2);
413 fclose(fp);
414 do_msg:
415 strcat(message2, "@");
416
417 strncat(message2, message, SH_MAXMSG - strlen(message2) -1);
418 message2[SH_MAXMSG-1] = '\0';
419 strcpy(message, message2);
420 return;
421}
422
423
424int
425main (int argc, char * argv[])
426{
427
428 char message[SH_MAXMSG] = "";
429 char clientcd[1024];
430 char serversock[256];
431 int status, size;
432 int num = 1;
433 int flag = 0;
434
435#ifdef HAVE_VSNPRINTF
436 status = snprintf(serversock, 256, _("%s/%s.sock"),
437 DEFAULT_PIDDIR, SH_INSTALL_NAME);
438#else
439 if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > 256)
440 {
441 status = -1;
442 }
443 else
444 {
445 status = sprintf (serversock, _("%s/%s.sock"),
446 DEFAULT_PIDDIR, SH_INSTALL_NAME);
447 }
448#endif
449
450 if ((status < 0) || (status > 255))
451 {
452 fprintf(stderr, _("ERROR: Path too long (maximum 255): %s/%s.sock\n"),
453 DEFAULT_PIDDIR, SH_INSTALL_NAME);
454 return (EXIT_FAILURE);
455 }
456
457 while (argc > 1 && argv[num][0] == '-')
458 {
459 switch (argv[num][1])
460 {
461 case 'h':
462 usage(argv[0]);
463 return (EXIT_SUCCESS);
464
465 case 'v':
466 ++verbose;
467 break;
468
469 case 's':
470 --argc; ++num;
471 if (argv[num] == NULL || argv[num][0] == '\0') {
472 usage(argv[0]);
473 fprintf(stderr, _("ERROR: -s: argument missing\n"));
474 return (EXIT_FAILURE);
475 } else {
476 if (strlen(argv[num]) > 255)
477 {
478 fprintf(stderr, _("ERROR: Path too long: %s\n"), argv[num]);
479 return (EXIT_FAILURE);
480 }
481 strncpy (serversock, argv[num], 256);
482 serversock[255] = '\0';
483 }
484 break;
485
486 case 'c':
487 --argc; ++num;
488 if (argv[num] == NULL || argv[num][0] == '\0') {
489 usage(argv[0]);
490 fprintf(stderr, _("ERROR: -c: argument missing\n"));
491 return (EXIT_FAILURE);
492 } else {
493 if (strlen(argv[num]) >= SH_MAXMSG)
494 {
495 fprintf(stderr, _("ERROR: Command too long: %s\n"),
496 argv[num]);
497 return (EXIT_FAILURE);
498 }
499 strncpy (message, argv[num], SH_MAXMSG);
500 message[SH_MAXMSG-1] = '\0';
501 strncat(message, ":", SH_MAXMSG-strlen(message)-1);
502 message[SH_MAXMSG-1] = '\0';
503 flag = 1;
504 }
505 break;
506
507 default:
508 usage(argv[0]);
509 fprintf(stderr, _("ERROR: unknown option -%c\n"), argv[num][1]);
510 return (EXIT_FAILURE);
511 }
512 --argc; ++num;
513 }
514
515 if (flag == 0) /* no command given */
516 {
517 usage(argv[0]);
518 return (EXIT_FAILURE);
519 }
520
521 if (argc > 1)
522 {
523 if (strlen(argv[num]) > (SH_MAXMSG - strlen(message) -1))
524 {
525 fprintf(stderr, _("ERROR: Hostname too long: %s\n"), argv[num]);
526 return (EXIT_FAILURE);
527 }
528 strncat (message, argv[num], SH_MAXMSG -strlen(message) - 1);
529 message[SH_MAXMSG-1] = '\0';
530 }
531 else
532 {
533 if (message[0] == 'P' && message[1] == 'R' &&
534 message[2] == 'O' && message[3] == 'B' && message[4] == 'E' )
535 {
536 strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
537 message[SH_MAXMSG-1] = '\0';
538 }
539 else if (message[0] == 'L' && message[1] == 'I' &&
540 message[2] == 'S' && message[3] == 'T')
541 {
542 strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
543 message[SH_MAXMSG-1] = '\0';
544 }
545 else
546 {
547 fprintf(stderr, _("ERROR: this command requires a hostname\n"));
548 usage(argv[0]);
549 return (EXIT_FAILURE);
550 }
551 }
552
553 fixup_message(message);
554
555 /* OpenBSD wants >= 1024
556 */
557 if (NULL == getcwd(clientcd, 1024))
558 {
559 perror(_("ERROR: getcwd"));
560 return (EXIT_FAILURE);
561 }
562 size = strlen(clientcd) + 1 + strlen(CLIENT) + 6;
563 sockname = malloc (size);
564 if (!sockname)
565 {
566 perror(_("ERROR: main: malloc"));
567 return (EXIT_FAILURE);
568 }
569#ifdef HAVE_VSNPRINTF
570 snprintf(sockname, size, _("%s/%s.sock"), clientcd, CLIENT);
571#else
572 sprintf(sockname, _("%s/%s.sock"), clientcd, CLIENT);
573#endif
574
575 /* Make the socket.
576 */
577 sock = make_named_socket (sockname);
578 if (sock < 0)
579 {
580 return (EXIT_FAILURE);
581 }
582
583 /* Set up termination handler.
584 */
585 signal (SIGINT, termination_handler);
586 signal (SIGHUP, termination_handler);
587 signal (SIGTERM, termination_handler);
588 signal (SIGQUIT, termination_handler);
589
590 /* Send the datagram.
591 */
592 status = send_to_server (serversock, message);
593 if (status < 0)
594 {
595 fprintf(stderr, _("ERROR: sending command to server failed\n"));
596 (void) termination_handler(0);
597 return (EXIT_FAILURE);
598 }
599
600 /* Wait for a reply.
601 */
602 if (message[0] == 'L' && message[1] == 'I' &&
603 message[2] == 'S' && message[3] == 'T')
604 {
605 if (verbose)
606 fprintf(stdout, _("# Waiting for listing.\n"));
607 }
608 else
609 {
610 if (verbose)
611 fprintf(stdout, _("# Waiting for confirmation.\n"));
612 }
613 status = recv_from_server (message);
614
615 if (status < 0)
616 {
617 fprintf(stderr, _("ERROR: receiving data from server failed.\n"));
618 (void) termination_handler(0);
619 return (EXIT_FAILURE);
620 }
621
622 /* Clean up. */
623 (void) termination_handler(0);
624 return (EXIT_SUCCESS);
625}
626
Note: See TracBrowser for help on using the repository browser.