source: trunk/src/yulectl.c@ 137

Last change on this file since 137 was 117, checked in by rainer, 17 years ago

Fix for ticket #73 (yulectl rejects 14 char password).
--This line, and th se below, will be ignored--

M src/yulectl.c
M docs/Changelog

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