/* Copyright (C) 2009 Trend Micro Inc. * All right reserved. * * This program is a free software; you can redistribute it * and/or modify it under the terms of the GNU General Public * License (version 2) as published by the FSF - Free Software * Foundation */ #include "shared.h" #include "remoted.h" #include "os_crypto/md5/md5_op.h" #include "os_net/os_net.h" #include /* Internal structures */ typedef struct _file_sum { int mark; char *name; os_md5 sum; } file_sum; /* Internal functions prototypes */ static void read_controlmsg(unsigned int agentid, char *msg); static int send_file_toagent(unsigned int agentid, const char *name, const char *sum); static void f_files(void); static void c_files(void); /* Global vars */ static file_sum **f_sum; static time_t _ctime; static time_t _stime; /* For the last message tracking */ static char *_msg[MAX_AGENTS + 1]; static char *_keep_alive[MAX_AGENTS + 1]; static int _changed[MAX_AGENTS + 1]; static int modified_agentid; /* pthread mutex variables */ static pthread_mutex_t lastmsg_mutex; static pthread_cond_t awake_mutex; /* Save a control message received from an agent * read_controlmsg (other thread) is going to deal with it * (only if message changed) */ void save_controlmsg(unsigned int agentid, char *r_msg) { char msg_ack[OS_FLSIZE + 1]; /* Reply to the agent */ snprintf(msg_ack, OS_FLSIZE, "%s%s", CONTROL_HEADER, HC_ACK); send_msg(agentid, msg_ack); /* Check if there is a keep alive already for this agent */ if (_keep_alive[agentid] && _msg[agentid] && (strcmp(_msg[agentid], r_msg) == 0)) { utimes(_keep_alive[agentid], NULL); } else if (strcmp(r_msg, HC_STARTUP) == 0) { return; } else { FILE *fp; char *uname = r_msg; char *random_leftovers; /* Lock mutex */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } /* Update rmsg */ if (_msg[agentid]) { free(_msg[agentid]); } os_strdup(r_msg, _msg[agentid]); /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } r_msg = strchr(r_msg, '\n'); if (!r_msg) { merror("%s: WARN: Invalid message from agent id: '%d'(uname)", ARGV0, agentid); return; } *r_msg = '\0'; random_leftovers = strchr(r_msg, '\n'); if (random_leftovers) { *random_leftovers = '\0'; } /* Update the keep alive */ if (!_keep_alive[agentid]) { char agent_file[OS_SIZE_1024 + 1]; agent_file[OS_SIZE_1024] = '\0'; /* Write to the agent file */ snprintf(agent_file, OS_SIZE_1024, "%s/%s-%s", AGENTINFO_DIR, keys.keyentries[agentid]->name, keys.keyentries[agentid]->ip->ip); os_strdup(agent_file, _keep_alive[agentid]); } /* Write to the file */ fp = fopen(_keep_alive[agentid], "w"); if (fp) { fprintf(fp, "%s\n", uname); fclose(fp); } else { merror("ossec-remoted: ERROR: Could not open %s: %s", _keep_alive[agentid], strerror(errno)); } } /* Lock now to notify of change */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } /* Assign new values */ _changed[agentid] = 1; modified_agentid = (int) agentid; /* Signal that new data is available */ pthread_cond_signal(&awake_mutex); /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } return; } /* Free the files memory */ static void f_files() { int i; if (!f_sum) { return; } for (i = 0;; i++) { if (f_sum[i] == NULL) { break; } if (f_sum[i]->name) { free(f_sum[i]->name); } free(f_sum[i]); f_sum[i] = NULL; } free(f_sum); f_sum = NULL; } /* Create the structure with the files and checksums */ static void c_files() { DIR *dp; struct dirent *entry; os_md5 md5sum; unsigned int f_size = 0; f_sum = NULL; /* Create merged file */ os_realloc(f_sum, (f_size + 2) * sizeof(file_sum *), f_sum); os_calloc(1, sizeof(file_sum), f_sum[f_size]); f_sum[f_size]->mark = 0; f_sum[f_size]->name = NULL; f_sum[f_size]->sum[0] = '\0'; MergeAppendFile(SHAREDCFG_FILE, NULL); f_size++; /* Open directory */ dp = opendir(SHAREDCFG_DIR); if (!dp) { merror("%s: Error opening directory: '%s': %s ", ARGV0, SHAREDCFG_DIR, strerror(errno)); return; } /* Read directory */ while ((entry = readdir(dp)) != NULL) { char tmp_dir[512]; /* Ignore . and .. */ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) { continue; } snprintf(tmp_dir, 512, "%s/%s", SHAREDCFG_DIR, entry->d_name); /* Leave the shared config file for later */ if (strcmp(tmp_dir, SHAREDCFG_FILE) == 0) { continue; } if (OS_MD5_File(tmp_dir, md5sum, OS_TEXT) != 0) { merror("%s: Error accessing file '%s'", ARGV0, tmp_dir); continue; } f_sum = (file_sum **)realloc(f_sum, (f_size + 2) * sizeof(file_sum *)); if (!f_sum) { ErrorExit(MEM_ERROR, ARGV0, errno, strerror(errno)); } f_sum[f_size] = (file_sum *) calloc(1, sizeof(file_sum)); if (!f_sum[f_size]) { ErrorExit(MEM_ERROR, ARGV0, errno, strerror(errno)); } strncpy(f_sum[f_size]->sum, md5sum, 32); os_strdup(entry->d_name, f_sum[f_size]->name); f_sum[f_size]->mark = 0; MergeAppendFile(SHAREDCFG_FILE, tmp_dir); f_size++; } if (f_sum != NULL) { f_sum[f_size] = NULL; } closedir(dp); if (OS_MD5_File(SHAREDCFG_FILE, md5sum, OS_TEXT) != 0) { merror("%s: Error accessing file '%s'", ARGV0, SHAREDCFG_FILE); f_sum[0]->sum[0] = '\0'; } strncpy(f_sum[0]->sum, md5sum, 32); os_strdup(SHAREDCFG_FILENAME, f_sum[0]->name); return; } /* Send a file to the agent * Returns -1 on error */ static int send_file_toagent(unsigned int agentid, const char *name, const char *sum) { int i = 0; size_t n = 0; char file[OS_SIZE_1024 + 1]; char buf[OS_SIZE_1024 + 1]; FILE *fp; snprintf(file, OS_SIZE_1024, "%s/%s", SHAREDCFG_DIR, name); fp = fopen(file, "r"); if (!fp) { merror(FOPEN_ERROR, ARGV0, file, errno, strerror(errno)); return (-1); } /* Send the file name first */ snprintf(buf, OS_SIZE_1024, "%s%s%s %s\n", CONTROL_HEADER, FILE_UPDATE_HEADER, sum, name); if (send_msg(agentid, buf) == -1) { merror(SEC_ERROR, ARGV0); fclose(fp); return (-1); } /* Send the file contents */ while ((n = fread(buf, 1, 900, fp)) > 0) { buf[n] = '\0'; if (send_msg(agentid, buf) == -1) { merror(SEC_ERROR, ARGV0); fclose(fp); return (-1); } /* Sleep 1 every 30 messages -- no flood */ if (i > 30) { sleep(1); i = 0; } i++; } /* Send the message to close the file */ snprintf(buf, OS_SIZE_1024, "%s%s", CONTROL_HEADER, FILE_CLOSE_HEADER); if (send_msg(agentid, buf) == -1) { merror(SEC_ERROR, ARGV0); fclose(fp); return (-1); } fclose(fp); return (0); } /* Read the available control message from the agent */ static void read_controlmsg(unsigned int agentid, char *msg) { int i; /* Remove uname */ msg = strchr(msg, '\n'); if (!msg) { merror("%s: Invalid message from '%d' (uname)", ARGV0, agentid); return; } *msg = '\0'; msg++; if (!f_sum) { /* Nothing to share with agent */ return; } /* Parse message */ while (*msg != '\0') { char *md5; char *file; md5 = msg; file = msg; msg = strchr(msg, '\n'); if (!msg) { merror("%s: Invalid message from '%s' (strchr \\n)", ARGV0, keys.keyentries[agentid]->ip->ip); break; } *msg = '\0'; msg++; file = strchr(file, ' '); if (!file) { merror("%s: Invalid message from '%s' (strchr ' ')", ARGV0, keys.keyentries[agentid]->ip->ip); break; } *file = '\0'; file++; /* New agents only have merged.mg */ if (strcmp(file, SHAREDCFG_FILENAME) == 0) { if (strcmp(f_sum[0]->sum, md5) != 0) { debug1("%s: DEBUG Sending file '%s' to agent.", ARGV0, f_sum[0]->name); if (send_file_toagent(agentid, f_sum[0]->name, f_sum[0]->sum) < 0) { merror("%s: ERROR: Unable to send file '%s' to agent.", ARGV0, f_sum[0]->name); } } i = 0; while (f_sum[i]) { f_sum[i]->mark = 0; i++; } return; } for (i = 1;; i++) { if (f_sum[i] == NULL) { break; } else if (strcmp(f_sum[i]->name, file) != 0) { continue; } else if (strcmp(f_sum[i]->sum, md5) != 0) { f_sum[i]->mark = 1; /* Marked to update */ } else { f_sum[i]->mark = 2; } break; } } /* Update each marked file */ for (i = 1;; i++) { if (f_sum[i] == NULL) { break; } if ((f_sum[i]->mark == 1) || (f_sum[i]->mark == 0)) { debug1("%s: Sending file '%s' to agent.", ARGV0, f_sum[i]->name); if (send_file_toagent(agentid, f_sum[i]->name, f_sum[i]->sum) < 0) { merror("%s: Error sending file '%s' to agent.", ARGV0, f_sum[i]->name); } } f_sum[i]->mark = 0; } return; } /* Wait for new messages to read * The messages will be sent using save_controlmsg */ void *wait_for_msgs(__attribute__((unused)) void *none) { int id; char msg[OS_SIZE_1024 + 2]; /* Initialize the memory */ memset(msg, '\0', OS_SIZE_1024 + 2); /* Should never leave this loop */ while (1) { unsigned int i; /* Every NOTIFY * 30 minutes, re-read the files * If something changed, notify all agents */ _ctime = time(0); if ((_ctime - _stime) > (NOTIFY_TIME * 30)) { f_files(); c_files(); _stime = _ctime; } /* Lock mutex */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return (NULL); } /* If no agent changed, wait for signal */ if (modified_agentid == -1) { pthread_cond_wait(&awake_mutex, &lastmsg_mutex); } /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return (NULL); } /* Check if any agent is ready */ for (i = 0; i < keys.keysize; i++) { /* If agent wasn't changed, try next */ if (_changed[i] != 1) { continue; } id = 0; /* Lock mutex */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); break; } if (_msg[i]) { /* Copy the message to be analyzed */ strncpy(msg, _msg[i], OS_SIZE_1024); _changed[i] = 0; if (modified_agentid >= (int) i) { modified_agentid = -1; } id = 1; } /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); break; } if (id) { read_controlmsg(i, msg); } } } return (NULL); } /* Should be called before anything here */ void manager_init(int isUpdate) { int i; _stime = time(0); f_files(); c_files(); debug1("%s: DEBUG: Running manager_init", ARGV0); modified_agentid = -1; for (i = 0; i < MAX_AGENTS + 1; i++) { _keep_alive[i] = NULL; _msg[i] = NULL; _changed[i] = 0; } /* Initialize mutexes */ if (isUpdate == 0) { pthread_mutex_init(&lastmsg_mutex, NULL); pthread_cond_init(&awake_mutex, NULL); } return; }