mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

rem2.c (10743B)


      1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
      2 
      3    Mixmaster may be redistributed and modified under certain conditions.
      4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
      5    ANY KIND, either express or implied. See the file COPYRIGHT for
      6    details.
      7 
      8    Process Mixmaster remailer messages
      9    $Id: rem2.c 934 2006-06-24 13:40:39Z rabbi $ */
     10 
     11 
     12 #include "mix3.h"
     13 #include <string.h>
     14 #include <time.h>
     15 #include <ctype.h>
     16 #include <sys/types.h>
     17 #include <sys/stat.h>
     18 #ifdef POSIX
     19 #include <unistd.h>
     20 #else /* end of POSIX */
     21 #include <io.h>
     22 #endif /* else if not POSIX */
     23 #ifndef _MSC
     24 #include <dirent.h>
     25 #endif /* not _MSC */
     26 #include <assert.h>
     27 
     28 int mix_dearmor(BUFFER *in, BUFFER *out)
     29 {
     30   BUFFER *line, *md;
     31   int tempbuf = 0;
     32   int err = 0;
     33 
     34   line = buf_new();
     35   md = buf_new();
     36 
     37   if (in == out) {
     38     tempbuf = 1;
     39     out = buf_new();
     40   }
     41   do {
     42     err = buf_getline(in, line);
     43     if (err == -1)
     44       goto end;
     45   }
     46   while (!bufeq(line, begin_remailer));
     47 
     48   do {
     49     /* skip lines before message digest */
     50     if (buf_getline(in, md) == -1)
     51       break;
     52   } while (strlen(md->data) != 24);
     53 
     54   decode(in, out);
     55 
     56   err = buf_getline(in, line);
     57   if (err != 0 || !bufeq(line, end_remailer))
     58     err = -1;
     59   else {
     60     digest_md5(out, line);
     61     encode(line, 0);
     62     if (!buf_eq(md, line))
     63       err = -1;
     64     if (out->length != 20480)
     65       err = -1;
     66   }
     67 
     68 end:
     69   if (err == -1)
     70     errlog(NOTICE, "Malformatted message.\n");
     71 
     72   if (tempbuf) {
     73     buf_move(in, out);
     74     buf_free(out);
     75   }
     76   buf_free(line);
     77   buf_free(md);
     78   return (err);
     79 }
     80 
     81 static int isnewid(BUFFER *id, long timestamp)
     82 /* return values:
     83  *   0: ignore message, no error
     84  *   1: ok, process message
     85  *  -1: bad message, send reply
     86  */
     87 {
     88   FILE *f;
     89   int ret = 1;
     90   long now, old = 0;
     91   LOCK *i = NULL;
     92   idlog_t idbuf;
     93 
     94   if (REMAIL == 0)
     95     return (1); /* don't keep statistics for the client */
     96 
     97   now = time(NULL);
     98 
     99   if ((f = mix_openfile(IDLOG, "rb+")) != NULL) {
    100     fread(&idbuf,1,sizeof(idlog_t),f);
    101     old = idbuf.time;
    102   } else {
    103     if (IDEXP == 0) {
    104       if (timestamp > 0 && timestamp <= now - 7 * SECONDSPERDAY) {
    105 	errlog(LOG, "Ignoring old message.\n");
    106 	return (0);
    107       }
    108     } else {
    109       if ((f = mix_openfile(IDLOG, "wb")) != NULL) {
    110 	memset(idbuf.id,0,sizeof(idbuf.id));
    111 	idbuf.time = now;
    112 	fwrite(&idbuf,1,sizeof(idlog_t),f);
    113 	memcpy(idbuf.id,id->data,sizeof(idbuf.id));
    114 	idbuf.time = now;
    115 	fwrite(&idbuf,1,sizeof(idlog_t),f);
    116 	fclose(f);
    117 	errlog(NOTICE, "Creating %s\n", IDLOG);
    118       } else {
    119 	errlog(ERRORMSG, "Can't create %s\n", IDLOG);
    120       }
    121       return (1);
    122     }
    123   }
    124 
    125   if (now - old < 5 * SECONDSPERDAY)	/* never reject messages less than */
    126     old = now - 5 * SECONDSPERDAY;	/* 5 days old (== minimum IDEXP) */
    127 
    128   if (timestamp > 0 && timestamp <= old) {
    129     errlog(LOG, "Ignoring old message.\n");
    130     ret = 0;
    131     goto end;
    132   }
    133   i = lockfile(IDLOG);
    134   while (fread(&idbuf, 1, sizeof(idlog_t), f) == sizeof(idlog_t)) {
    135     if (!memcmp(idbuf.id, id->data, sizeof(idbuf.id))) {
    136       char idstr[33];
    137       id_encode(id->data, idstr);
    138       errlog(LOG, "Ignoring redundant message: %s.\n", idstr);
    139       ret = 0;
    140       goto end;
    141     }
    142   }
    143   if (timestamp > now) {
    144     errlog(LOG, "Ignoring message with future timestamp.\n");
    145     ret = -1;
    146     goto end;
    147   }
    148   if (ftell(f)%sizeof(idlog_t)) fseek(f,0-(ftell(f)%sizeof(idlog_t)),SEEK_CUR); /* make sure that we're on sizeof(idlog_t) byte boundary */
    149   memcpy(idbuf.id,id->data,sizeof(idbuf.id));
    150   idbuf.time = now;
    151   fwrite(&idbuf,1,sizeof(idlog_t),f);
    152 end:
    153   if (i)
    154     unlockfile(i);
    155   fclose(f);
    156   return (ret);
    157 }
    158 
    159 int mix2_decrypt(BUFFER *m)
    160      /*  0: ok
    161       * -1: error
    162       * -2: old message */
    163 {
    164   int err = 0;
    165   int i;
    166   BUFFER *privkey;
    167   BUFFER *keyid;
    168   BUFFER *dec, *deskey;
    169   BUFFER *packetid, *mid, *digest, *addr, *temp, *iv, *ivvec;
    170   int type, packet = 0, numpackets = 0, timestamp = 0;
    171   BUFFER *body;
    172   BUFFER *header, *out;
    173 
    174   privkey = buf_new();
    175   keyid = buf_new();
    176   dec = buf_new();
    177   deskey = buf_new();
    178   packetid = buf_new();
    179   mid = buf_new();
    180   digest = buf_new();
    181   addr = buf_new();
    182   temp = buf_new();
    183   iv = buf_new();
    184   ivvec = buf_new();
    185   body = buf_new();
    186   header = buf_new();
    187   out = buf_new();
    188 
    189   buf_get(m, keyid, 16);
    190   err = db_getseckey(keyid->data, privkey);
    191   if (err == -1)
    192     goto end;
    193   buf_get(m, deskey, buf_getc(m));
    194   err = pk_decrypt(deskey, privkey);
    195   if (err == -1 || deskey->length != 24) {
    196     err = -1;
    197     errlog(NOTICE, "Cannot decrypt message.\n");
    198     goto end;
    199   }
    200   buf_get(m, iv, 8);
    201   buf_get(m, dec, 328);
    202   buf_crypt(dec, deskey, iv, DECRYPT);
    203   buf_get(dec, packetid, 16);
    204   buf_get(dec, deskey, 24);
    205   type = buf_getc(dec);
    206   switch (type) {
    207   case 0:
    208     buf_get(dec, ivvec, 152);
    209     buf_get(dec, addr, 80);
    210     break;
    211   case 1:
    212     buf_get(dec, mid, 16);
    213     buf_get(dec, iv, 8);
    214     break;
    215   case 2:
    216     packet = buf_getc(dec);
    217     numpackets = buf_getc(dec);
    218     buf_get(dec, mid, 16);
    219     buf_get(dec, iv, 8);
    220     break;
    221   default:
    222     errlog(WARNING, "Unknown message type.\n");
    223     err = -1;
    224     goto end;
    225   }
    226   if (dec->data[dec->ptr] == '0' && dec->data[dec->ptr + 1] == '0' &&
    227       dec->data[dec->ptr + 2] == '0' && dec->data[dec->ptr + 3] == '0' &&
    228       dec->data[dec->ptr + 4] == '\0') {
    229     dec->ptr += 5;
    230     timestamp = buf_geti_lo(dec);
    231   } else {
    232     errlog(LOG, "Ignoring message without timestamp.\n");
    233     err = -1;
    234     goto end;
    235   }
    236   buf_get(dec, digest, 16);
    237 
    238   dec->length = dec->ptr - 16;	/* ignore digest */
    239   dec->ptr = dec->length;
    240 
    241   if (!isdigest_md5(dec, digest)) {
    242     errlog(NOTICE, "Message digest does not match.\n");
    243     err = -1;
    244     goto end;
    245   }
    246   switch (isnewid(packetid, timestamp * SECONDSPERDAY)) {
    247     case  0: err = -2; /* redundant message */
    248 	     goto end;
    249     case -1: err = -1; /* future timestamp */
    250 	     goto end; 
    251   }
    252   buf_append(body, m->data + 20 * 512, 10240);
    253 
    254   switch (type) {
    255   case 0:
    256     buf_chop(addr);
    257     buf_cat(out, addr);
    258     buf_nl(out);
    259     for (i = 0; i < 19; i++) {
    260       buf_reset(header);
    261       buf_append(header, m->data + (i + 1) * 512, 512);
    262       buf_reset(iv);
    263       buf_append(iv, ivvec->data + i * 8, 8);
    264       buf_crypt(header, deskey, iv, DECRYPT);
    265       buf_cat(out, header);
    266     }
    267     buf_reset(header);
    268     buf_pad(header, 512);
    269     buf_cat(out, header);
    270     buf_reset(iv);
    271     buf_append(iv, ivvec->data + 144, 8);
    272     buf_crypt(body, deskey, iv, DECRYPT);
    273     buf_cat(out, body);
    274     mix_pool(out, INTERMEDIATE, -1);
    275     break;
    276   case 1:
    277     buf_crypt(body, deskey, iv, DECRYPT);
    278     err = v2body_setlen(body);
    279     if (err == -1)
    280       goto end;
    281     assert(body->ptr == 4);
    282     v2body(body);
    283     break;
    284   case 2:
    285     buf_crypt(body, deskey, iv, DECRYPT);
    286     v2partial(body, mid, packet, numpackets);
    287     break;
    288   }
    289 end:
    290   buf_free(privkey);
    291   buf_free(keyid);
    292   buf_free(dec);
    293   buf_free(deskey);
    294   buf_free(packetid);
    295   buf_free(mid);
    296   buf_free(digest);
    297   buf_free(addr);
    298   buf_free(temp);
    299   buf_free(iv);
    300   buf_free(ivvec);
    301   buf_free(body);
    302   buf_free(header);
    303   buf_free(out);
    304 
    305   return (err);
    306 }
    307 
    308 int v2body_setlen(BUFFER *body)
    309 {
    310   long length;
    311 
    312   length = buf_getl_lo(body);
    313   if (length < 0 || length > body->length)
    314     return (-1);
    315   body->length = length + 4;
    316   return (0);
    317 }
    318 
    319 int v2body(BUFFER *body)
    320 {
    321   int i, n;
    322   BUFFER *to, *newsgroups;
    323   BUFFER *temp, *out;
    324   BUFFER *line;
    325   int type = MSG_MAIL;
    326   int subject = 0;
    327 
    328   line = buf_new();
    329   to = buf_new();
    330   newsgroups = buf_new();
    331   temp = buf_new();
    332   out = buf_new();
    333 
    334   n = buf_getc(body);
    335   for (i = 0; i < n; i++) {
    336     buf_get(body, line, 80);
    337     buf_chop(line);
    338     if (bufileft(line, "null:"))
    339       goto end;
    340     if (bufileft(line, "post:")) {
    341       type = MSG_POST;
    342       if (line->length > 5) {
    343 	int j = 5;
    344 
    345 	while (j < line->length && isspace(line->data[j]))
    346 	  j++;
    347 	if (newsgroups->length > 0)
    348 	  buf_appends(newsgroups, ",");
    349 	buf_append(newsgroups, line->data + j, line->length - j);
    350       }
    351     } else {
    352       if (to->length > 0)
    353 	buf_appends(to, ",");
    354       buf_cat(to, line);
    355     }
    356   }
    357   if (to->length > 0) {
    358     buf_appends(out, "To: ");
    359     buf_cat(out, to);
    360     buf_nl(out);
    361   }
    362   if (newsgroups->length > 0) {
    363     buf_appends(out, "Newsgroups: ");
    364     buf_cat(out, newsgroups);
    365     buf_nl(out);
    366   }
    367   n = buf_getc(body);
    368   for (i = 0; i < n; i++) {
    369     buf_get(body, line, 80);
    370     buf_chop(line);
    371     if (bufileft(line, "Subject:"))
    372       subject = 1;
    373     buf_cat(out, line);
    374     buf_nl(out);
    375   }
    376 
    377   buf_rest(temp, body);
    378   buf_uncompress(temp);
    379   buf_set(body, temp);
    380   buf_reset(temp);
    381 
    382   if (buf_lookahead(body, line) == 0 && isline(line, HASHMARK)) {
    383     buf_getline(body, line);
    384     while (buf_getline(body, line) == 0) {
    385       if (bufileft(line, "subject:"))
    386 	subject = 1;
    387       buf_cat(out, line);
    388       buf_nl(out);
    389     }
    390   }
    391   if (type == MSG_POST && !subject)
    392     buf_appends(out, "Subject: (no subject)\n");
    393 
    394   buf_nl(out);
    395   buf_rest(out, body);
    396   buf_reset(body);
    397   mix_pool(out, type, -1);
    398 
    399 end:
    400   buf_free(line);
    401   buf_free(to);
    402   buf_free(newsgroups);
    403   buf_free(temp);
    404   buf_free(out);
    405   return (0);
    406 }
    407 
    408 int v2_merge(BUFFER *mid)
    409 {
    410   char fname[PATHMAX], line[LINELEN];
    411   BUFFER *temp, *msg;
    412   FILE *l, *f;
    413   int i, numpackets;
    414   struct stat sb;
    415   long d;
    416   int n;
    417   int err = -1;
    418 
    419   temp = buf_new();
    420   msg = buf_new();
    421   pool_packetfile(fname, mid, 0);
    422   l = fopen(fname, "a+");
    423   if (l != NULL)
    424     lock(l);
    425 
    426   pool_packetfile(fname, mid, 1);
    427   f = fopen(fname, "rb");
    428   if (f == NULL)
    429     goto end;
    430   fscanf(f, "%32s %ld %d %d\n", line, &d, &i, &numpackets);
    431   fclose(f);
    432 
    433   /* do we have all packets? */
    434   for (i = 1; i <= numpackets; i++) {
    435     pool_packetfile(fname, mid, i);
    436     if (stat(fname, &sb) != 0)
    437       goto end;
    438   }
    439   errlog(LOG, "Reassembling multipart message.\n");
    440   for (i = 1; i <= numpackets; i++) {
    441     pool_packetfile(fname, mid, i);
    442     f = fopen(fname, "rb");
    443     if (f == NULL)
    444       goto end;
    445     fscanf(f, "%32s %ld %d %d\n", line, &d, &n, &n);
    446     buf_clear(temp);
    447     buf_read(temp, f);
    448     v2body_setlen(temp);
    449     buf_append(msg, temp->data + 4, temp->length - 4);
    450     fclose(f);
    451     unlink(fname);
    452   }
    453   err = v2body(msg);
    454 
    455 end:
    456   if (l != NULL)
    457     fclose(l);
    458   pool_packetfile(fname, mid, 0);
    459   unlink(fname);
    460   buf_free(temp);
    461   buf_free(msg);
    462   return (err);
    463 }
    464 
    465 int v2partial(BUFFER *m, BUFFER *mid, int packet, int numpackets)
    466 {
    467   char fname[PATHMAX], idstr[33];
    468   FILE *f;
    469   int err = 1;
    470 
    471   pool_packetfile(fname, mid, packet);
    472   f = fopen(fname, "wb");
    473   if (f == NULL) {
    474     err = -1;
    475     goto end;
    476   }
    477   id_encode(mid->data, idstr);
    478   fprintf(f, "%s %ld %d %d\n", idstr, (long) time(NULL), packet,
    479 	  numpackets);
    480   buf_write(m, f);
    481   buf_reset(m);
    482   fclose(f);
    483   v2_merge(mid);
    484 end:
    485   return (err);
    486 }