/* @(#) $Id: sec.c,v 1.52 2007/07/20 00:19:26 dcid Exp $ */ /* Copyright (C) 2003-2007 Daniel B. Cid * All rights 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 3) as published by the FSF - Free Software * Foundation. * * License details at the LICENSE file included with OSSEC or * online at: http://www.ossec.net/en/licensing.html */ #include "shared.h" #include "headers/sec.h" #include "os_zlib/os_zlib.h" #include "os_crypto/md5/md5_op.h" #include "os_crypto/blowfish/bf_op.h" /** Remote ID directory */ #ifndef WIN32 #define RIDS_DIR "/queue/rids" #else #define RIDS_DIR "rids" #endif #define SENDER_COUNTER "sender_counter" #define KEYSIZE 128 /** Sending counts **/ unsigned int global_count = 0; unsigned int local_count = 0; /** Average compression rates **/ int evt_count = 0; int rcv_count = 0; unsigned int c_orig_size = 0; unsigned int c_comp_size = 0; /** Static variables (read from define file) **/ int _s_comp_print = 0; int _s_recv_flush = 0; /** Function Prototypes **/ void StartCounter(keystruct *keys); /* _MemClear v0.1 - Internal use */ void _MemClear(char *id, char *name, char *ip, char *key) { memset(id,'\0', KEYSIZE +1); memset(name,'\0',KEYSIZE +1); memset(key,'\0', KEYSIZE +1); memset(ip,'\0', KEYSIZE +1); } /* _CHash v0.1 -Internal use */ void _CHash(keystruct *keys, char *id, char *name, char *ip, char *key) { int v_ip; os_md5 filesum1; os_md5 filesum2; char *tmp_str; char _finalstr[KEYSIZE]; struct sockaddr_in peer; /* Allocating for the whole structure */ keys->ids = (char **)realloc(keys->ids, (keys->keysize+1) * sizeof(char *)); keys->ips = (os_ip **)realloc(keys->ips, (keys->keysize+1)* sizeof(os_ip *)); keys->name = (char **)realloc(keys->name, (keys->keysize+1)* sizeof(char*)); keys->peer_info = realloc(keys->peer_info, (keys->keysize+1) * sizeof(peer)); keys->global = realloc(keys->global, (keys->keysize+1) * sizeof(int)); keys->local = realloc(keys->local, (keys->keysize+1) * sizeof(int)); keys->rcvd = realloc(keys->rcvd, (keys->keysize+1) * sizeof(int)); keys->fps = realloc(keys->fps, (keys->keysize+2) * sizeof(FILE *)); if(!keys->ids || !keys->ips || !keys->peer_info || !keys->global|| !keys->local || !keys->rcvd || !keys->name || !keys->fps) { ErrorExit(MEM_ERROR, __local_name); } /* Setting configured values */ keys->ids[keys->keysize] = strdup(id); os_calloc(1, sizeof(os_ip), keys->ips[keys->keysize]); if((v_ip = OS_IsValidIP(ip, keys->ips[keys->keysize])) == 0) { ErrorExit(INVALID_IP, ARGV0, ip); } /* We need to remove the "/" from the cidr */ if((tmp_str = strchr(keys->ips[keys->keysize]->ip, '/')) != NULL) { *tmp_str = '\0'; } keys->name[keys->keysize] = strdup(name); /* Initializing the variables */ keys->local[keys->keysize] = 0; keys->global[keys->keysize] = 0; keys->rcvd[keys->keysize] = 0; if(!keys->ids[keys->keysize] || !keys->ips[keys->keysize] || !keys->name[keys->keysize]) { ErrorExit(MEM_ERROR, __local_name); } /* Generating key */ /* MD5 from name, id and key */ OS_MD5_Str(name, filesum1); OS_MD5_Str(id, filesum2); /* Generating new filesum1 */ snprintf(_finalstr, sizeof(_finalstr)-1, "%s%s",filesum1,filesum2); /* Using just half of the first md5 (user/id) */ OS_MD5_Str(_finalstr, filesum1); filesum1[15] = '\0'; filesum1[16] = '\0'; OS_MD5_Str(key, filesum2); /* Generating final key */ memset(_finalstr,'\0', sizeof(_finalstr)); snprintf(_finalstr, 49, "%s%s", filesum2, filesum1); /* Final key is 48 * 4 = 192bits */ keys->keys = (char **)realloc(keys->keys,(keys->keysize+1)*sizeof(char *)); if(keys->keys == NULL) ErrorExit(MEM_ERROR, __local_name); keys->keys[keys->keysize]=strdup(_finalstr); if(keys->keys[keys->keysize] == NULL) ErrorExit(MEM_ERROR, __local_name); /* Cleaning final string from memory */ memset(_finalstr,'\0', sizeof(_finalstr)); /* next */ keys->keysize++; return; } /* int CheckKeys(): Checks if the authentication keys are present */ int CheckKeys() { FILE *fp; if(File_DateofChange(KEYSFILE_PATH) < 0) { merror(NO_AUTHFILE, __local_name, KEYSFILE_PATH); merror(NO_REM_CONN, __local_name); return(0); } fp = fopen(KEYSFILE_PATH, "r"); if(!fp) { /* We can leave from here */ merror(FOPEN_ERROR, __local_name, KEYSFILE_PATH); merror(NO_AUTHFILE, __local_name, KEYSFILE_PATH); merror(NO_REM_CONN, __local_name); return(0); } fclose(fp); /* Authentication keys are present */ return(1); } /* ReadKeys v0.1: 2005/02/01 */ void ReadKeys(keystruct *keys, int just_read) { FILE *fp; char buffer[OS_BUFFER_SIZE +1]; char name[KEYSIZE +1]; char ip[KEYSIZE +1]; char id[KEYSIZE +1]; char key[KEYSIZE +1]; if(File_DateofChange(KEYS_FILE) < 0) { merror(NO_AUTHFILE, __local_name, KEYS_FILE); ErrorExit(NO_REM_CONN, __local_name); } fp = fopen(KEYS_FILE,"r"); if(!fp) { /* We can leave from here */ merror(FOPEN_ERROR, __local_name, KEYS_FILE); ErrorExit(NO_REM_CONN, __local_name); } /* Initializing structure */ keys->ids = NULL; keys->keys = NULL; keys->ips = NULL; keys->global = NULL; keys->local = NULL; keys->name = NULL; keys->rcvd = NULL; keys->peer_info = NULL; keys->fps = NULL; keys->keysize = 0; _MemClear(id, name, ip, key); memset(buffer, '\0', OS_BUFFER_SIZE +1); /* Reading each line. * lines are divided on id name ip key */ while(fgets(buffer, OS_BUFFER_SIZE, fp) != NULL) { char *tmp_str; char *valid_str; if((buffer[0] == '#') || (buffer[0] == ' ')) continue; /* Getting ID */ valid_str = buffer; tmp_str = strchr(buffer, ' '); if(!tmp_str) { merror(INVALID_KEY, __local_name, buffer); continue; } *tmp_str = '\0'; tmp_str++; strncpy(id, valid_str, KEYSIZE -1); /* Getting name */ valid_str = tmp_str; tmp_str = strchr(tmp_str, ' '); if(!tmp_str) { merror(INVALID_KEY, __local_name, buffer); } *tmp_str = '\0'; tmp_str++; strncpy(name, valid_str, KEYSIZE -1); /* Getting ip address */ valid_str = tmp_str; tmp_str = strchr(tmp_str, ' '); if(!tmp_str) { merror(INVALID_KEY, __local_name, buffer); } *tmp_str = '\0'; tmp_str++; strncpy(ip, valid_str, KEYSIZE -1); /* Getting key */ valid_str = tmp_str; tmp_str = strchr(tmp_str, '\n'); if(tmp_str) { *tmp_str = '\0'; } strncpy(key, valid_str, KEYSIZE -1); /* Generating the key hash */ _CHash(keys, id, name, ip, key); /* Clearing the memory */ _MemClear(id, name, ip, key); continue; } fclose(fp); /* clear one last time before leaving */ _MemClear(id,name,ip,key); /* Checking if there is any agent available */ if(keys->keysize == 0) { ErrorExit(NO_REM_CONN, __local_name); } /* Opening count files */ if(!just_read) { /* Reading the counters */ StartCounter(keys); } return; } /* IsAllowedIP v0.1: 2005/02/09 */ int IsAllowedIP(keystruct *keys, char *srcip) { int i = 0; if(srcip == NULL) return(-1); for(i = 0; i < keys->keysize; i++) { if(strcmp(keys->ips[i]->ip, srcip) == 0) return(i); } return(-1); } /* IsAllowedID v0.1: 2005/11/19 */ int IsAllowedID(keystruct *keys, char *id) { int i = 0; if(id == NULL) return(-1); for(i = 0; i < keys->keysize; i++) { if(strcmp(keys->ids[i],id) == 0) return(i); } return(-1); } int IsAllowedName(keystruct *keys, char *name) { int i = 0; for(i = 0; i < keys->keysize; i++) { if(strcmp(keys->name[i], name) == 0) return(i); } return(-1); } int IsAllowedDynamicID(keystruct *keys, char *id, char *srcip) { int i = 0; if(id == NULL) return(-1); for(i = 0; i < keys->keysize; i++) { if((strcmp(keys->ids[i],id) == 0) && (OS_IPFound(srcip, keys->ips[i]))) return(i); } return(-1); } /* StartCounter and read saved values */ void StartCounter(keystruct *keys) { int i; char rids_file[OS_FLSIZE +1]; rids_file[OS_FLSIZE] = '\0'; /* Starting receiving counter */ for(i = 0; i<=keys->keysize; i++) { /* On i == keysize, we deal with the * sender counter. */ if(i == keys->keysize) { snprintf(rids_file, OS_FLSIZE, "%s/%s", RIDS_DIR, SENDER_COUNTER); } else { snprintf(rids_file, OS_FLSIZE, "%s/%s", RIDS_DIR, keys->ids[i]); } keys->fps[i] = fopen(rids_file, "r+"); /* If nothing is there, try to open as write only */ if(!keys->fps[i]) { keys->fps[i] = fopen(rids_file, "w"); if(!keys->fps[i]) { ErrorExit(FOPEN_ERROR, __local_name, rids_file); } } else { unsigned int g_c = 0, l_c = 0; if(fscanf(keys->fps[i],"%u:%u", &g_c, &l_c) != 2) { if(i == keys->keysize) { verbose("%s: No previous sender counter.", __local_name); } else { verbose("%s: No previous counter available for '%s'.", __local_name, keys->name[i]); } g_c = 0; l_c = 0; } if(i == keys->keysize) { verbose("%s: Assigning sender counter: %d:%d", __local_name, g_c, l_c); global_count = g_c; local_count = l_c; } else { verbose("%s: Assigning counter for agent %s: '%d:%d'.", __local_name, keys->name[i], g_c, l_c); keys->global[i] = g_c; keys->local[i] = l_c; } } } /* Getting counter values */ _s_recv_flush = getDefine_Int("remoted", "recv_counter_flush", 10, 999999); /* Average printout values */ _s_comp_print = getDefine_Int("remoted", "comp_average_printout", 10, 999999); } /** RemoveCounter(char *id) * Remove the ID counter. */ void RemoveCounter(char *id) { char rids_file[OS_FLSIZE +1]; snprintf(rids_file, OS_FLSIZE, "%s/%s",RIDS_DIR, id); unlink(rids_file); } /** StoreSenderCounter((keystruct *keys, int global, int local) * Store sender counter. */ void StoreSenderCounter(keystruct *keys, int global, int local) { /* Writting at the beginning of the file */ fseek(keys->fps[keys->keysize], 0, SEEK_SET); fprintf(keys->fps[keys->keysize], "%u:%u:", global, local); } /* StoreCount(keystruct *keys, int id, int global, int local) * Store the global and local count of events. */ void StoreCounter(keystruct *keys, int id, int global, int local) { /* Writting at the beginning of the file */ fseek(keys->fps[id], 0, SEEK_SET); fprintf(keys->fps[id], "%u:%u:", global, local); } /* CheckSum v0.1: 2005/02/15 * Verify the checksum of the message. * Returns NULL on error or the message on success. */ char *CheckSum(char *msg) { os_md5 recvd_sum; os_md5 checksum; /* Better way */ strncpy(recvd_sum,msg,32); recvd_sum[32]='\0'; msg+=32; OS_MD5_Str(msg, checksum); if(strncmp(checksum,recvd_sum,32) != 0) { return(NULL); } return(msg); } /* ReadSecMSG v0.2: 2005/02/10 */ char *ReadSecMSG(keystruct *keys, char *buffer, char *cleartext, int id, int buffer_size) { int cmp_size; unsigned int msg_global; unsigned int msg_local; char *f_msg; if(*buffer == ':') { buffer++; } else { merror(ENCFORMAT_ERROR, __local_name, keys->ips[id]->ip); return(NULL); } /* Decrypting message */ if(!OS_BF_Str(buffer, cleartext, keys->keys[id], buffer_size, OS_DECRYPT)) { merror(ENCKEY_ERROR, __local_name, keys->ips[id]); return(NULL); } /* Compressed */ else if(cleartext[0] == '!') { cleartext[buffer_size] = '\0'; cleartext++; buffer_size--; /* Removing padding */ while(*cleartext == '!') { cleartext++; buffer_size--; } /* Uncompressing */ cmp_size = os_uncompress(cleartext, buffer, buffer_size, OS_MAXSTR); if(!cmp_size) { merror(UNCOMPRESS_ERR, __local_name); return(NULL); } /* Checking checksum */ f_msg = CheckSum(buffer); if(f_msg == NULL) { merror(ENCSUM_ERROR, __local_name, keys->ips[id]); return(NULL); } /* Removing random */ f_msg+=5; /* Checking count -- protecting against replay attacks */ msg_global = atoi(f_msg); f_msg+=10; /* Checking for the right message format */ if(*f_msg != ':') { merror(ENCFORMAT_ERROR, __local_name, keys->ips[id]->ip); return(NULL); } f_msg++; msg_local = atoi(f_msg); f_msg+=5; if((msg_global > keys->global[id])|| ((msg_global == keys->global[id]) && (msg_local > keys->local[id]))) { /* Updating currently counts */ keys->global[id] = msg_global; keys->local[id] = msg_local; if(rcv_count >= _s_recv_flush) { StoreCounter(keys, id, msg_global, msg_local); rcv_count = 0; } rcv_count++; return(f_msg); } /* Checking if it is a duplicated message */ if(msg_global == keys->global[id]) { return(NULL); } /* Warn about duplicated messages */ merror("%s: Duplicate error: global: %d, local: %d, " "saved global: %d, saved local:%d", __local_name, msg_global, msg_local, keys->global[id], keys->local[id]); merror(ENCTIME_ERROR, __local_name, keys->name[id]); return(NULL); } /* Old format */ else if(cleartext[0] == ':') { int msg_count; time_t msg_time; /* Closing string */ cleartext[buffer_size] = '\0'; /* Checking checksum */ cleartext++; f_msg = CheckSum(cleartext); if(f_msg == NULL) { merror(ENCSUM_ERROR, __local_name, keys->ips[id]); return(NULL); } /* Checking time -- protecting against replay attacks */ msg_time = atoi(f_msg); f_msg+=11; msg_count = atoi(f_msg); f_msg+=5; if((msg_time > keys->global[id]) || ((msg_time == keys->global[id])&&(msg_count > keys->local[id]))) { /* Updating currently time and count */ keys->global[id] = msg_time; keys->local[id] = msg_count; f_msg = strchr(f_msg, ':'); if(f_msg) { f_msg++; return(f_msg); } } /* Checking if it is a duplicated message */ if((msg_count == keys->local[id]) && (msg_time == keys->global[id])) { return(NULL); } /* Warn about duplicated message */ merror("%s: Duplicate error: msg_count: %d, time: %d, " "saved count: %d, saved_time:%d", __local_name, msg_count, msg_time, keys->local[id], keys->global[id]); merror(ENCTIME_ERROR, __local_name, keys->name[id]); return(NULL); } merror(ENCFORMAT_ERROR, __local_name, keys->ips[id]->ip); return(NULL); } /* Creat a encrypted message. * Returns the size of it */ int CreateSecMSG(keystruct *keys, char *msg, char *msg_encrypted, int id) { int bfsize; int msg_size; int cmp_size; u_int16_t rand1; char _tmpmsg[OS_MAXSTR + 2]; char _finmsg[OS_MAXSTR + 2]; os_md5 md5sum; msg_size = strlen(msg); /* Checking for invalid msg sizes */ if((msg_size > (OS_MAXSTR - OS_HEADER_SIZE))||(msg_size < 1)) { merror(ENCSIZE_ERROR, __local_name, msg); return(0); } /* Random number */ rand1 = (u_int16_t)rand(); _tmpmsg[OS_MAXSTR +1] = '\0'; _finmsg[OS_MAXSTR +1] = '\0'; msg_encrypted[OS_MAXSTR] = '\0'; /* Increasing local and global counters */ if(local_count >= 9997) { local_count = 0; global_count++; } local_count++; snprintf(_tmpmsg, OS_MAXSTR,"%05hu%010u:%04hu:%s", rand1, global_count, local_count, msg); /* Generating md5sum of the unencrypted string */ OS_MD5_Str(_tmpmsg, md5sum); /* Generating final msg to be compressed */ snprintf(_finmsg, OS_MAXSTR,"%s%s",md5sum,_tmpmsg); msg_size = strlen(_finmsg); /* Compressing message. * We assing the first 8 bytes for padding. */ cmp_size = os_compress(_finmsg, _tmpmsg + 8, msg_size, OS_MAXSTR - 12); if(!cmp_size) { merror(COMPRESS_ERR, __local_name, _finmsg); return(0); } cmp_size++; /* Padding the message (needs to be div by 8) */ bfsize = 8 - (cmp_size % 8); if(bfsize == 8) bfsize = 0; _tmpmsg[0] = '!'; _tmpmsg[1] = '!'; _tmpmsg[2] = '!'; _tmpmsg[3] = '!'; _tmpmsg[4] = '!'; _tmpmsg[5] = '!'; _tmpmsg[6] = '!'; _tmpmsg[7] = '!'; cmp_size+=bfsize; /* Getting average sizes */ c_orig_size+= msg_size; c_comp_size+= cmp_size; if(evt_count > _s_comp_print) { verbose("%s: Event count after '%u': %u->%u (%d%%)", __local_name, evt_count, c_orig_size, c_comp_size, (c_comp_size * 100)/c_orig_size); evt_count = 0; c_orig_size = 0; c_comp_size = 0; } evt_count++; /* If the ip is dynamic (not single host, append agent id * to the message. */ if(!isSingleHost(keys->ips[id]) && isAgent) { snprintf(msg_encrypted, 16, "!%s!:", keys->ids[id]); msg_size = strlen(msg_encrypted); } else { /* Setting beginning of the message */ msg_encrypted[0] = ':'; msg_size = 1; } /* msg_size is the ammount of non-encrypted message * appended to the buffer. On dynamic ips, it will * include the agent id. */ /* Encrypting everything */ OS_BF_Str(_tmpmsg + (7 - bfsize), msg_encrypted + msg_size, keys->keys[id], cmp_size, OS_ENCRYPT); /* Storing before leaving */ StoreSenderCounter(keys, global_count, local_count); return(cmp_size + msg_size); } /* EOF */