chain2.c (20338B)
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 Encrypt message for Mixmaster chain 9 $Id: chain2.c 934 2006-06-24 13:40:39Z rabbi $ */ 10 11 12 #include "mix3.h" 13 #include <string.h> 14 #include <stdlib.h> 15 #include <time.h> 16 #include <ctype.h> 17 #include <assert.h> 18 19 #define N(X) (isdigit(X) ? (X)-'0' : 0) 20 21 int prepare_type2list(BUFFER *out) 22 { 23 FILE *list; 24 char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN], 25 version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN]; 26 int assigned; 27 time_t created, expires; 28 29 list = mix_openfile(PUBRING, "r"); 30 if (list == NULL) 31 return (-1); 32 while (fgets(line, sizeof(line), list) != NULL) { 33 if (strleft(line, begin_key)) { 34 while (fgets(line, sizeof(line), list) != NULL && 35 !strleft(line, end_key)) ; 36 } else if (strlen(line) > 36 && line[0] != '#') { 37 assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s", 38 name, addr, keyid, version, flags, createdstr, expiresstr); 39 if (assigned < 4) 40 continue; 41 if (assigned >= 6) { 42 created = parse_yearmonthday(createdstr); 43 if (created == 0 || created == -1) { 44 errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid); 45 continue; 46 }; 47 if (created > time(NULL)) { 48 errlog(WARNING, "Key %s created in the future.\n", keyid); 49 continue; 50 }; 51 } 52 if (assigned >= 7) { 53 expires = parse_yearmonthday(expiresstr); 54 if (expires == 0 || expires == -1) { 55 errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid); 56 continue; 57 }; 58 if (expires < time(NULL)) { 59 errlog(WARNING, "Key %s has expired.\n", keyid); 60 continue; 61 }; 62 } 63 buf_appends(out, line); 64 } 65 } 66 fclose(list); 67 return (0); 68 } 69 70 int mix2_rlist(REMAILER remailer[], int badchains[MAXREM][MAXREM]) 71 { 72 FILE *list, *excl; 73 int n, i, listed = 0; 74 75 char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN], 76 version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN]; 77 int assigned; 78 time_t created, expires; 79 BUFFER *starex; 80 81 starex = buf_new(); 82 excl = mix_openfile(STAREX, "r"); 83 if (excl != NULL) { 84 buf_read(starex, excl); 85 fclose(excl); 86 } 87 88 list = mix_openfile(PUBRING, "r"); 89 if (list == NULL) { 90 buf_free(starex); 91 return (-1); 92 } 93 for (n = 1; fgets(line, sizeof(line), list) != NULL && n < MAXREM;) 94 if (strleft(line, begin_key)) { 95 while (fgets(line, sizeof(line), list) != NULL && 96 !strleft(line, end_key)) ; 97 } else if (strlen(line) > 36 && line[0] != '#') { 98 flags[0] = '\0'; 99 assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s", 100 name, addr, keyid, version, flags, createdstr, expiresstr); 101 if (assigned < 4) 102 continue; 103 if (assigned >= 6) { 104 created = parse_yearmonthday(createdstr); 105 if (created == 0 || created == -1) { 106 errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid); 107 continue; 108 }; 109 if (created > time(NULL)) { 110 errlog(WARNING, "Key %s created in the future.\n", keyid); 111 continue; 112 }; 113 } 114 if (assigned >= 7) { 115 expires = parse_yearmonthday(expiresstr); 116 if (expires == 0 || expires == -1) { 117 errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid); 118 continue; 119 }; 120 if (expires < time(NULL)) { 121 errlog(WARNING, "Key %s has expired.\n", keyid); 122 continue; 123 }; 124 } 125 strncpy(remailer[n].name, name, sizeof(remailer[n].name)); 126 remailer[n].name[sizeof(remailer[n].name) - 1] = '\0'; 127 strncpy(remailer[n].addr, addr, sizeof(remailer[n].addr)); 128 remailer[n].addr[sizeof(remailer[n].addr) - 1] = '\0'; 129 remailer[n].flags.mix = 1; 130 remailer[n].flags.cpunk = 0; 131 remailer[n].flags.nym = 0; 132 remailer[n].flags.newnym = 0; 133 id_decode(keyid, remailer[n].keyid); 134 remailer[n].version = N(version[0]); 135 remailer[n].flags.compress = strfind(flags, "C"); 136 remailer[n].flags.post = strfind(flags, "N"); 137 remailer[n].flags.middle = strfind(flags, "M"); 138 remailer[n].info[0].reliability = 0; 139 remailer[n].info[0].latency = 0; 140 remailer[n].info[0].history[0] = '\0'; 141 remailer[n].flags.star_ex = bufifind(starex, name); 142 n++; 143 } 144 fclose(list); 145 list = mix_openfile(TYPE2REL, "r"); 146 if (list != NULL) { 147 while (fgets(line, sizeof(line), list) != NULL && 148 !strleft(line, "--------------------------------------------")) { 149 if (strleft(line, "Last update:")) { 150 int generated; 151 int now = time(NULL); 152 char *tmp = line + strlen("Last update:") + 1; 153 generated = parsedate(tmp); 154 if (generated == -1) { 155 /* For some weird reason, this isn't rfc822 */ 156 if (strleft(tmp, "Mon") || 157 strleft(tmp, "Tue") || 158 strleft(tmp, "Wed") || 159 strleft(tmp, "Thu") || 160 strleft(tmp, "Fri") || 161 strleft(tmp, "Sat") || 162 strleft(tmp, "Sun")) 163 tmp += 3; 164 generated = parsedate(tmp); 165 } 166 now = time(NULL); 167 if (generated != -1 && generated < now - SECONDSPERDAY) 168 errlog(WARNING, "Remailer Reliability Statistics are older than one day (check your clock?).\n"); 169 if (generated != -1 && generated > now) 170 errlog(WARNING, "Remailer Reliability Statistics are from the future (check your clock?).\n"); 171 } 172 }; 173 while (fgets(line, sizeof(line), list) != NULL && 174 !strleft(line, "</PRE>")) 175 if (strlen(line) >= 44 && strlen(line) <= 46) 176 for (i = 1; i < n; i++) 177 if (strleft(line, remailer[i].name) && 178 line[strlen(remailer[i].name)] == ' ') { 179 strncpy(remailer[i].info[0].history, line + 15, 12); 180 remailer[i].info[0].history[12] = '\0'; 181 remailer[i].info[0].reliability = 10000 * N(line[37]) 182 + 1000 * N(line[38]) + 100 * N(line[39]) 183 + 10 * N(line[41]) + N(line[42]); 184 remailer[i].info[0].latency = 36000 * N(line[28]) 185 + 3600 * N(line[29]) + 600 * N(line[31]) 186 + 60 * N(line[32]) + 10 * N(line[34]) 187 + N(line[35]); 188 listed++; 189 } 190 fclose(list); 191 } 192 193 parse_badchains(badchains, TYPE2REL, "Broken type-II remailer chains", remailer, n); 194 if (listed < 4) /* we have no valid reliability info */ 195 for (i = 1; i < n; i++) 196 remailer[i].info[0].reliability = 10000; 197 buf_free(starex); 198 return (n); 199 } 200 201 static int send_packet(int numcopies, BUFFER *packet, int chain[], 202 int chainlen, int packetnum, int numpackets, 203 BUFFER *mid, REMAILER remailer[], int badchains[MAXREM][MAXREM], 204 int maxrem, char *redirect_to, int ignore_constraints_if_necessary, 205 BUFFER *feedback) 206 /* 207 * Puts a mix packet in the pool. 208 * 209 * numcopies ... how often to put this packet into the pool 210 * i.e. send it. required that random remailers are in the chain. 211 * packet ... the payload, 10240 bytes in size. 212 * chain ... the chain to send this message along 213 * chainlen ... length of the chain 214 * packetnum ... in multi-packet messages (fragmented) the serial of this packet 215 * numpackets ... the total number of packets 216 * mid ... the message ID (required for fragmented packets 217 * remailer ... information about remailers, their reliabilities, capabilities, etc. 218 * badchains ... broken chain information 219 * maxrem ... the number of remailers in remailer[] and badchains[] 220 * redirect_to ... if this is not-null it needs to be an email address. 221 * in this case packet needs to be not only the body, but a 222 * complete mixmaster packet of 20480 bytes in size (20 headers + body). 223 * the chain given is prepended to the one already encrypted in 224 * the existing message. If this exceeds the allowed 20 hops in total 225 * the message is corrupted, the last node will realize this. 226 * This is useful if you want to reroute an existing mixmaster message 227 * that has foo as the next hop via a chain so that the packet will 228 * actually flow hop1,hop2,hop3,foo,.... 229 * ignore_constraints_if_necessary .. to be used when randhopping messages. 230 * if a chain can not be constructed otherwhise, maxlat, minlat, 231 * and minrel are ignored. 232 * feedback ... a buffer to write feedback to 233 */ 234 { 235 BUFFER *pid, *out, *header, *other, *encrypted, *key, *body; 236 BUFFER *iv, *ivarray, *temp; 237 BUFFER *pubkey; 238 char addr[LINELEN]; 239 int thischain[20]; 240 int hop; 241 int c, i; 242 int timestamp = 0; 243 int israndom = 0; 244 int err = 1; 245 246 body = buf_new(); 247 pid = buf_new(); 248 out = buf_new(); 249 header = buf_new(); 250 other = buf_new(); 251 key = buf_new(); 252 encrypted = buf_new(); 253 iv = buf_new(); 254 ivarray = buf_new(); 255 temp = buf_new(); 256 257 if (redirect_to != NULL) { 258 assert(packet->length == 20480); 259 buf_append(header, packet->data, 10240); 260 buf_append(temp, packet->data + 10240, 10240); 261 buf_clear(packet); 262 buf_cat(packet, temp); 263 } else 264 assert(packet->length == 10240); 265 266 buf_setrnd(pid, 16); 267 268 for (c = 0; c < numcopies; c++) { 269 buf_set(body, packet); 270 271 for (hop = 0; hop < chainlen; hop++) 272 thischain[hop] = chain[hop]; 273 274 israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen, 0, ignore_constraints_if_necessary); 275 if (israndom == -1) { 276 err = -1; 277 clienterr(feedback, "No reliable remailers!"); 278 } 279 if ((numcopies > 1 || numpackets > 1) && !israndom && (chainlen != 1)) { 280 clienterr(feedback, 281 "Multi-packet message without random remailers!"); 282 err = -1; 283 goto end; 284 } 285 for (hop = 0; hop < chainlen; hop++) { 286 switch (remailer[thischain[hop]].version) { 287 case 2: 288 case 3: /* not implemented yet; fall back to version 2 */ 289 /* create header chart to be encrypted with the session key */ 290 if (numcopies > 1 && hop == 0 && redirect_to == NULL) 291 buf_set(encrypted, pid); /* same ID only at final hop */ 292 else 293 buf_setrnd(encrypted, 16); 294 buf_setrnd(key, 24); /* key for encrypting the body */ 295 buf_cat(encrypted, key); 296 buf_setrnd(iv, 8); /* IV for encrypting the body */ 297 298 if (hop > 0 || redirect_to != NULL) { 299 /* IVs for header chart encryption */ 300 buf_setrnd(ivarray, 18 * 8); 301 buf_cat(ivarray, iv); /* 19th IV equals the body IV */ 302 303 buf_appendc(encrypted, 0); 304 buf_cat(encrypted, ivarray); 305 memset(addr, 0, 80); 306 if (hop == 0) { 307 assert(redirect_to != NULL); 308 strncpy(addr, redirect_to, 80); 309 } else { 310 assert(hop > 0); 311 strcpy(addr, remailer[thischain[hop - 1]].addr); 312 }; 313 buf_append(encrypted, addr, 80); 314 } else { 315 if (numpackets == 1) 316 buf_appendc(encrypted, 1); 317 else { 318 buf_appendc(encrypted, 2); 319 buf_appendc(encrypted, (byte) packetnum); 320 buf_appendc(encrypted, (byte) numpackets); 321 } 322 buf_cat(encrypted, mid); 323 buf_cat(encrypted, iv); /* body encryption IV */ 324 } 325 326 /* timestamp */ 327 buf_appends(encrypted, "0000"); 328 buf_appendc(encrypted, '\0'); /* timestamp magic */ 329 timestamp = time(NULL) / SECONDSPERDAY - rnd_number(4); 330 buf_appendi_lo(encrypted, timestamp); 331 332 /* message digest for this header */ 333 digest_md5(encrypted, temp); 334 buf_cat(encrypted, temp); 335 buf_pad(encrypted, 328); 336 337 /* encrypt message body */ 338 buf_crypt(body, key, iv, ENCRYPT); 339 340 if (hop > 0 || redirect_to != NULL) { 341 /* encrypt the other header charts */ 342 buf_clear(other); 343 for (i = 0; i < 19; i++) { 344 buf_clear(iv); 345 buf_clear(temp); 346 buf_append(iv, ivarray->data + 8 * i, 8); 347 buf_append(temp, header->data + 512 * i, 512); 348 buf_crypt(temp, key, iv, ENCRYPT); 349 buf_cat(other, temp); 350 } 351 } else 352 buf_setrnd(other, 19 * 512); /* fill with random data */ 353 354 /* create session key and IV to encrypt the header ... */ 355 buf_setrnd(key, 24); 356 buf_setrnd(iv, 8); 357 buf_crypt(encrypted, key, iv, ENCRYPT); 358 pubkey = buf_new(); 359 err = db_getpubkey(remailer[thischain[hop]].keyid, pubkey); 360 if (err == -1) 361 goto end; 362 err = pk_encrypt(key, pubkey); /* ... and encrypt the 363 session key */ 364 buf_free(pubkey); 365 if (err == -1 || key->length != 128) { 366 clienterr(feedback, "Encryption failed!"); 367 err = -1; 368 goto end; 369 } 370 /* now build the new header */ 371 buf_clear(header); 372 buf_append(header, remailer[thischain[hop]].keyid, 16); 373 buf_appendc(header, 128); 374 buf_cat(header, key); 375 buf_cat(header, iv); 376 buf_cat(header, encrypted); 377 buf_pad(header, 512); 378 buf_cat(header, other); 379 break; 380 default: 381 err = -1; 382 goto end; 383 } 384 } 385 386 /* build the message */ 387 buf_sets(out, remailer[thischain[chainlen - 1]].addr); 388 buf_nl(out); 389 buf_cat(out, header); 390 buf_cat(out, body); 391 assert(header->length == 10240 && body->length == 10240); 392 mix_pool(out, INTERMEDIATE, -1); 393 394 if (feedback) { 395 for (hop = chainlen - 1; hop >= 0; hop--) { 396 buf_appends(feedback, remailer[thischain[hop]].name); 397 if (hop > 0) 398 buf_appendc(feedback, ','); 399 } 400 buf_nl(feedback); 401 } 402 } 403 end: 404 buf_free(pid); 405 buf_free(body); 406 buf_free(out); 407 buf_free(header); 408 buf_free(temp); 409 buf_free(other); 410 buf_free(key); 411 buf_free(encrypted); 412 buf_free(iv); 413 buf_free(ivarray); 414 return (err); 415 } 416 417 int redirect_message(BUFFER *sendmsg, char *chainstr, int numcopies, BUFFER *feedback) 418 { 419 BUFFER *field; 420 BUFFER *content; 421 BUFFER *line; 422 char recipient[80] = ""; 423 int num = 0; 424 int err = 0; 425 int c; 426 int hop; 427 428 REMAILER remailer[MAXREM]; 429 int chain[20]; 430 int thischain[20]; 431 int chainlen; 432 int badchains[MAXREM][MAXREM]; 433 int maxrem; 434 int tempchain[20]; 435 int tempchainlen; 436 int israndom; 437 438 field = buf_new(); 439 content = buf_new(); 440 line = buf_new(); 441 442 if (numcopies == 0) 443 numcopies = NUMCOPIES; 444 if (numcopies > 10) 445 numcopies = 10; 446 447 /* Find the recipient */ 448 while (buf_getheader(sendmsg, field, content) == 0) 449 if (bufieq(field, "to")) { 450 strncpy(recipient, content->data, sizeof(recipient)); 451 num++; 452 }; 453 if (num != 1) { 454 clienterr(feedback, "Did not find exactly one To: address!"); 455 err = -1; 456 goto end; 457 }; 458 459 /* Dearmor the message */ 460 err = mix_dearmor(sendmsg, sendmsg); 461 if (err == -1) 462 goto end; 463 assert (sendmsg->length == 20480); 464 465 /* Check the chain */ 466 maxrem = mix2_rlist(remailer, badchains); 467 if (maxrem < 1) { 468 clienterr(feedback, "No remailer list!"); 469 err = -1; 470 goto end; 471 } 472 chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line); 473 if (chainlen < 1) { 474 if (line->length) 475 clienterr(feedback, line->data); 476 else 477 clienterr(feedback, "Invalid remailer chain!"); 478 err = -1; 479 goto end; 480 } else if (chainlen >= 20) { 481 clienterr(feedback, "A chainlength of 20 will certainly destroy the message!"); 482 err = -1; 483 goto end; 484 }; 485 486 487 for (c = 0; c < numcopies; c++) { 488 /* if our recipient is a remailer we want to make sure we're not using a known broken chain. 489 * therefore we need to pick the final remailer with care */ 490 for (hop = 0; hop < chainlen; hop++) 491 thischain[hop] = chain[hop]; 492 if (thischain[0] == 0) { 493 /* Find out, if recipient is a remailer */ 494 tempchainlen = chain_select(tempchain, recipient, maxrem, remailer, 0, line); 495 if (tempchainlen < 1 && line->length == 0) { 496 /* recipient is apparently not a remailer we know about */ 497 ; 498 } else { 499 /* Build a new chain, based on the one we already selected but 500 * with the recipient as the final hop. 501 * This is so that chain_rand properly selects nodes based on 502 * broken chains and DISTANCE */ 503 assert(chainlen < 20); 504 for (hop = 0; hop < chainlen; hop++) 505 thischain[hop+1] = thischain[hop]; 506 thischain[0] = tempchain[0]; 507 508 israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen + 1, 0, 0); 509 if (israndom == -1) { 510 err = -1; 511 clienterr(feedback, "No reliable remailers!"); 512 goto end; 513 } 514 515 /* Remove the added recipient hop */ 516 for (hop = 0; hop < chainlen; hop++) 517 thischain[hop] = thischain[hop + 1]; 518 }; 519 }; 520 521 /* queue the packet */ 522 if (send_packet(1, sendmsg, thischain, chainlen, 523 -1, -1, NULL, 524 remailer, badchains, maxrem, recipient, 0, feedback) == -1) 525 err = -1; 526 }; 527 528 end: 529 buf_free(field); 530 buf_free(content); 531 buf_free(line); 532 return (err); 533 } 534 535 int mix2_encrypt(int type, BUFFER *message, char *chainstr, int numcopies, 536 int ignore_constraints_if_necessary, 537 BUFFER *feedback) 538 { 539 /* returns 0 on success, -1 on error. feedback contains the selected 540 remailer chain or an error message 541 542 ignore_constraints_if_necessary .. to be used when randhopping messages. 543 if a chain can not be constructed otherwhise, 544 maxlat, minlat, and minrel are ignored. 545 */ 546 547 REMAILER remailer[MAXREM]; 548 int badchains[MAXREM][MAXREM]; 549 int maxrem; 550 BUFFER *line, *field, *content, *header, *msgdest, *msgheader, *body, 551 *temp, *mid; 552 byte numdest = 0, numhdr = 0; 553 char hdrline[LINELEN]; 554 BUFFER *packet; 555 int chain[20]; 556 int chainlen; 557 int i; 558 int err = 0; 559 560 mix_init(NULL); 561 packet = buf_new(); 562 line = buf_new(); 563 field = buf_new(); 564 content = buf_new(); 565 msgheader = buf_new(); 566 msgdest = buf_new(); 567 body = buf_new(); 568 temp = buf_new(); 569 mid = buf_new(); 570 header = buf_new(); 571 if (feedback) 572 buf_reset(feedback); 573 574 if (numcopies == 0) 575 numcopies = NUMCOPIES; 576 if (numcopies > 10) 577 numcopies = 10; 578 579 maxrem = mix2_rlist(remailer, badchains); 580 if (maxrem < 1) { 581 clienterr(feedback, "No remailer list!"); 582 err = -1; 583 goto end; 584 } 585 chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line); 586 if (chainlen < 1) { 587 if (line->length) 588 clienterr(feedback, line->data); 589 else 590 clienterr(feedback, "Invalid remailer chain!"); 591 err = -1; 592 goto end; 593 } 594 if (chain[0] == 0) 595 chain[0] = chain_randfinal(type, remailer, badchains, maxrem, 0, chain, chainlen, ignore_constraints_if_necessary); 596 597 if (chain[0] == -1) { 598 clienterr(feedback, "No reliable remailers!"); 599 err = -1; 600 goto end; 601 } 602 switch (remailer[chain[0]].version) { 603 case 2: 604 if (type == MSG_NULL) { 605 memset(hdrline, 0, 80); 606 strcpy(hdrline, "null:"); 607 buf_append(msgdest, hdrline, 80); 608 numdest++; 609 } else 610 while (buf_getheader(message, field, content) == 0) { 611 if (bufieq(field, "to")) { 612 memset(hdrline, 0, 80); 613 strncpy(hdrline, content->data, 80); 614 buf_append(msgdest, hdrline, 80); 615 numdest++; 616 } else if (type == MSG_POST && bufieq(field, "newsgroups")) { 617 memset(hdrline, 0, 80); 618 strcpy(hdrline, "post: "); 619 strcatn(hdrline, content->data, 80); 620 buf_append(msgdest, hdrline, 80); 621 numdest++; 622 } else { 623 buf_clear(header); 624 buf_appendheader(header, field, content); 625 hdr_encode(header, 80); 626 while (buf_getline(header, line) == 0) { 627 /* paste in encoded header entry */ 628 memset(hdrline, 0, 80); 629 strncpy(hdrline, line->data, 80); 630 buf_append(msgheader, hdrline, 80); 631 numhdr++; 632 } 633 } 634 } 635 buf_appendc(body, numdest); 636 buf_cat(body, msgdest); 637 buf_appendc(body, numhdr); 638 buf_cat(body, msgheader); 639 640 if (type != MSG_NULL) { 641 buf_rest(temp, message); 642 if (temp->length > 10236 && remailer[chain[0]].flags.compress) 643 buf_compress(temp); 644 buf_cat(body, temp); 645 buf_reset(temp); 646 } 647 buf_setrnd(mid, 16); /* message ID */ 648 for (i = 0; i <= body->length / 10236; i++) { 649 long length; 650 651 length = body->length - i * 10236; 652 if (length > 10236) 653 length = 10236; 654 buf_clear(packet); 655 buf_appendl_lo(packet, length); 656 buf_append(packet, body->data + i * 10236, length); 657 buf_pad(packet, 10240); 658 if (send_packet(numcopies, packet, chain, chainlen, 659 i + 1, body->length / 10236 + 1, 660 mid, remailer, badchains, maxrem, NULL, ignore_constraints_if_necessary, feedback) == -1) 661 err = -1; 662 } 663 break; 664 case 3: 665 NOT_IMPLEMENTED; 666 break; 667 default: 668 fprintf(stderr, "%d\n", chain[0]); 669 clienterr(feedback, "Unknown remailer version!"); 670 err = -1; 671 } 672 673 end: 674 buf_free(packet); 675 buf_free(line); 676 buf_free(field); 677 buf_free(content); 678 buf_free(header); 679 buf_free(msgheader); 680 buf_free(msgdest); 681 buf_free(body); 682 buf_free(temp); 683 buf_free(mid); 684 return (err); 685 }