tchain2.c - mixmaster - mixmaster 3.0 patched for libressl
HTML git clone git://parazyd.org/mixmaster.git
DIR Log
DIR Files
DIR Refs
DIR README
---
tchain2.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 }