/* SAMHAIN file system integrity testing */ /* Copyright (C) 2003 Rainer Wichmann */ /* */ /* This program is free software; you can redistribute it */ /* and/or modify */ /* it under the terms of the GNU General Public License as */ /* published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config_xor.h" #include #include #include #include #include #include #include #include #include #include #include #if !defined(AF_FILE) #define AF_FILE AF_UNIX #endif #define SH_MAXMSG 209 static int sock = -1; static char * sockname = NULL; static char password[15] = ""; static int verbose = 0; #ifdef SH_STEALTH char * globber(const char * string); #define _(string) globber(string) #define N_(string) string #else #define _(string) string #define N_(string) string #endif #ifdef SH_STEALTH #ifndef SH_MAX_GLOBS #define SH_MAX_GLOBS 32 #endif char * globber(const char * str) { register int i, j; static int count = -1; static char glob[SH_MAX_GLOBS][128]; ++count; if (count > (SH_MAX_GLOBS-1) ) count = 0; j = strlen(str); if (j > 127) j = 127; for (i = 0; i < j; ++i) { if (str[i] != '\n' && str[i] != '\t') glob[count][i] = str[i] ^ XOR_CODE; else glob[count][i] = str[i]; } glob[count][j] = '\0'; return glob[count]; } #endif #define CLIENT _("yulectl") int make_named_socket (char * sockname) { int sock; #if 0 struct sockaddr_un name; size_t size; #else (void) sockname; #endif /* Create the socket. */ sock = socket (PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror (_("ERROR: socket")); return -1; } #if 0 /* Bind a name to the socket. */ name.sun_family = AF_FILE; strcpy (name.sun_path, sockname); /* The size of the address is the offset of the start of the filename, plus its length, plus one for the terminating null byte. */ size = (offsetof (struct sockaddr_un, sun_path) + strlen (name.sun_path) + 1); if (bind (sock, (struct sockaddr *) &name, size) < 0) { perror (_("ERROR: bind")); return -1; } #endif return sock; } void termination_handler (int signum) { /* Clean up. */ if (signum != 0) { if (verbose) fprintf(stdout, _("# Terminated on signal %d\n"), signum); } #if 0 if (sockname != NULL) unlink (sockname); #endif if (sock >= 0 ) close (sock); return; } int send_to_server (char * serversock, char * message) { struct sockaddr_un name; /* size_t size; */ int size; int nbytes; /* Initialize the server socket address. */ name.sun_family = AF_UNIX; strncpy (name.sun_path, serversock, sizeof(name.sun_path) - 1); size = (offsetof (struct sockaddr_un, sun_path) + strlen (name.sun_path) + 1); nbytes = connect(sock, (struct sockaddr *) & name, size); if (nbytes < 0) { perror (_("ERROR: connect")); return -1; } /* Send the datagram. nbytes = sendto (sock, message, strlen (message) + 1, 0, (struct sockaddr *) & name, size); */ nbytes = send (sock, message, strlen (message) + 1, 0); if (nbytes < 0) { perror (_("ERROR: send")); return -1; } return 0; } static int getline_from_server (int sock, char * buf, int size) { int nbytes = 0; int status = 0; char * p = buf; do { status = read (sock, p, 1); if (status <= 0) { buf[nbytes] = '\0'; return ((status == 0) ? nbytes : status); } else if (*p == '\0') { return nbytes; } ++nbytes; ++p; } while (nbytes < size); buf[size-1] = '\0'; return 0; } int recv_from_server (char * message) { int nbytes = 0; char recvmsg[SH_MAXMSG]; int num = 0; int good = -1; int islist = 0; char * p; if (password[0] == '\0') { if (message[0] == 'L' && message[1] == 'I' && message[2] == 'S' && message[3] == 'T') { islist = 1; } if (message[0] == 'P' && message[1] == 'R' && message[2] == 'O' && message[3] == 'B' && message[4] == 'E' ) { islist = 1; } } else { p = &message[strlen(password)+1]; if (p[0] == 'L' && p[1] == 'I' && p[2] == 'S' && p[3] == 'T') { islist = 1; } if (p[0] == 'P' && p[1] == 'R' && p[2] == 'O' && p[3] == 'B' && p[4] == 'E' ) { islist = 1; } } if (islist == 1) { do { /* nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0); */ nbytes = getline_from_server (sock, recvmsg, SH_MAXMSG); if (nbytes < 0) { if (errno == EAGAIN) { return 0; } else { perror (_("ERROR: recv")); return -1; } } else if (nbytes == 0) { return 0; } if (recvmsg[0] == 'E' && recvmsg[1] == 'N' && recvmsg[2] == 'D') { if (verbose && (num == 0)) fprintf (stdout, _("# There are no pending commands.\n")); return 0; } ++num; fprintf (stdout, _("%03d: %s\n"), num, recvmsg); } while (nbytes >= 0); } else { /* nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0); */ nbytes = recv (sock, recvmsg, SH_MAXMSG, 0); if (nbytes < 0) { perror (_("ERROR: recv")); return -1; } } /* Print a diagnostic message. */ if (password[0] == '\0') { good = strcmp (message, recvmsg); } else { good = strcmp (&message[strlen(password)+1], recvmsg); } if (0 != good) { fprintf (stderr, _("ERROR: Bounced message != original message (possible reason: superfluous password).\n")); return -1; } else { if (verbose) fprintf (stdout, _("# Message received by server.\n")); } return 0; } void usage(char * name) { printf(_("\nUsage : %s [-v][-s server_socket] -c command \n\n"), name); printf(_("Purpose : send commands to the server via a socket,\n")); printf(_(" in particular commands that the server would\n")); printf(_(" transfer to the client when\n")); printf(_(" this client connects to deliver a message.\n\n")); printf(_(" If password is required, it is read from\n")); printf(_(" $HOME/.yulectl_cred or taken from the environment\n")); printf(_(" variable YULECTL_PASSWORD (not recommended).\n\n")); printf(_("Commands: RELOAD \n")); printf(_(" STOP \n")); printf(_(" CANCEL \n")); printf(_(" LIST \n")); printf(_(" LISTALL \n")); printf(_(" PROBE \n")); return; } char * rtrim(char * str) { size_t len; if (!str) return str; len = strlen(str); while (len > 0) { --len; if (str[len] == '\n' || str[len] == '\r') str[len] = '\0'; else break; } return str; } void fixup_message (char * message) { char message2[SH_MAXMSG]; char home[4096]; FILE * fp; struct passwd * pwent; char * pw; pw = getenv(_("YULECTL_PASSWORD")); if (pw && strlen(pw) < 15) { strcpy(password, pw); strcpy(message2, password); goto do_msg; } pwent = getpwuid(geteuid()); if ((pwent == 0) || (pwent->pw_dir == NULL)) { if (verbose) fprintf (stderr, _("WARNING: no home directory for euid %ld\n"), (long) geteuid()); if (NULL != getenv(_("HOME"))) { strncpy(home, getenv(_("HOME")), 4096); home[4095] = '\0'; } else { fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid()); exit(EXIT_FAILURE); } } else { strncpy(home, pwent->pw_dir, 4096); home[4095] = '\0'; } if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > 4096) { fprintf (stderr, _("ERROR: path for $HOME is too long.\n")); exit(EXIT_FAILURE); } strcat(home, _("/.yulectl_cred")); fp = fopen(home, "r"); if (fp == NULL) { if (verbose && (errno == ENOENT)) fprintf (stdout, _("# No password file (%s) exists\n"), home); else if (verbose) fprintf (stdout, _("# Password file (%s) not accessible for euid %ld uid %ld\n"), home, (long)geteuid(), (long)getuid()); return; } if (NULL == fgets(message2, sizeof(message2), fp)) { fprintf (stderr, _("ERROR: empty or unreadable password file (%s).\n"), home); exit(EXIT_FAILURE); } (void) rtrim(message2); if (strlen(message2) > 14) { fprintf (stderr, _("ERROR: Password too long (max. 14 characters).\n")); exit(EXIT_FAILURE); } strcpy(password, message2); fclose(fp); do_msg: strcat(message2, "@"); strncat(message2, message, SH_MAXMSG - strlen(message2) -1); message2[SH_MAXMSG-1] = '\0'; strcpy(message, message2); return; } int main (int argc, char * argv[]) { char message[SH_MAXMSG] = ""; char clientcd[1024]; char serversock[256]; int status, size; int num = 1; int flag = 0; #ifdef HAVE_VSNPRINTF status = snprintf(serversock, 256, _("%s/%s.sock"), DEFAULT_PIDDIR, SH_INSTALL_NAME); #else if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > 256) { status = -1; } else { status = sprintf (serversock, _("%s/%s.sock"), DEFAULT_PIDDIR, SH_INSTALL_NAME); } #endif if ((status < 0) || (status > 255)) { fprintf(stderr, _("ERROR: Path too long (maximum 255): %s/%s.sock\n"), DEFAULT_PIDDIR, SH_INSTALL_NAME); return (EXIT_FAILURE); } while (argc > 1 && argv[num][0] == '-') { switch (argv[num][1]) { case 'h': usage(argv[0]); return (EXIT_SUCCESS); case 'v': ++verbose; break; case 's': --argc; ++num; if (argv[num] == NULL || argv[num][0] == '\0') { usage(argv[0]); fprintf(stderr, _("ERROR: -s: argument missing\n")); return (EXIT_FAILURE); } else { if (strlen(argv[num]) > 255) { fprintf(stderr, _("ERROR: Path too long: %s\n"), argv[num]); return (EXIT_FAILURE); } strncpy (serversock, argv[num], 256); serversock[255] = '\0'; } break; case 'c': --argc; ++num; if (argv[num] == NULL || argv[num][0] == '\0') { usage(argv[0]); fprintf(stderr, _("ERROR: -c: argument missing\n")); return (EXIT_FAILURE); } else { if (strlen(argv[num]) >= SH_MAXMSG) { fprintf(stderr, _("ERROR: Command too long: %s\n"), argv[num]); return (EXIT_FAILURE); } strncpy (message, argv[num], SH_MAXMSG); message[SH_MAXMSG-1] = '\0'; strncat(message, ":", SH_MAXMSG-strlen(message)-1); message[SH_MAXMSG-1] = '\0'; flag = 1; } break; default: usage(argv[0]); fprintf(stderr, _("ERROR: unknown option -%c\n"), argv[num][1]); return (EXIT_FAILURE); } --argc; ++num; } if (flag == 0) /* no command given */ { usage(argv[0]); return (EXIT_FAILURE); } if (argc > 1) { if (strlen(argv[num]) > (SH_MAXMSG - strlen(message) -1)) { fprintf(stderr, _("ERROR: Hostname too long: %s\n"), argv[num]); return (EXIT_FAILURE); } strncat (message, argv[num], SH_MAXMSG -strlen(message) - 1); message[SH_MAXMSG-1] = '\0'; } else { if (message[0] == 'P' && message[1] == 'R' && message[2] == 'O' && message[3] == 'B' && message[4] == 'E' ) { strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1); message[SH_MAXMSG-1] = '\0'; } else if (message[0] == 'L' && message[1] == 'I' && message[2] == 'S' && message[3] == 'T') { strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1); message[SH_MAXMSG-1] = '\0'; } else { fprintf(stderr, _("ERROR: this command requires a hostname\n")); usage(argv[0]); return (EXIT_FAILURE); } } fixup_message(message); /* OpenBSD wants >= 1024 */ if (NULL == getcwd(clientcd, 1024)) { perror(_("ERROR: getcwd")); return (EXIT_FAILURE); } size = strlen(clientcd) + 1 + strlen(CLIENT) + 6; sockname = malloc (size); if (!sockname) { perror(_("ERROR: main: malloc")); return (EXIT_FAILURE); } #ifdef HAVE_VSNPRINTF snprintf(sockname, size, _("%s/%s.sock"), clientcd, CLIENT); #else sprintf(sockname, _("%s/%s.sock"), clientcd, CLIENT); #endif /* Make the socket. */ sock = make_named_socket (sockname); if (sock < 0) { return (EXIT_FAILURE); } /* Set up termination handler. */ signal (SIGINT, termination_handler); signal (SIGHUP, termination_handler); signal (SIGTERM, termination_handler); signal (SIGQUIT, termination_handler); /* Send the datagram. */ status = send_to_server (serversock, message); if (status < 0) { fprintf(stderr, _("ERROR: sending command to server failed\n")); (void) termination_handler(0); return (EXIT_FAILURE); } /* Wait for a reply. */ if (message[0] == 'L' && message[1] == 'I' && message[2] == 'S' && message[3] == 'T') { if (verbose) fprintf(stdout, _("# Waiting for listing.\n")); } else { if (verbose) fprintf(stdout, _("# Waiting for confirmation.\n")); } status = recv_from_server (message); if (status < 0) { fprintf(stderr, _("ERROR: receiving data from server failed.\n")); (void) termination_handler(0); return (EXIT_FAILURE); } /* Clean up. */ (void) termination_handler(0); return (EXIT_SUCCESS); }