tidying
[exim.git] / src / src / dkim.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2017 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Code for DKIM support. Other DKIM relevant code is in
9    receive.c, transport.c and transports/smtp.c */
10
11 #include "exim.h"
12
13 #ifndef DISABLE_DKIM
14
15 #include "pdkim/pdkim.h"
16
17 int dkim_verify_oldpool;
18 pdkim_ctx *dkim_verify_ctx = NULL;
19 pdkim_signature *dkim_signatures = NULL;
20 pdkim_signature *dkim_cur_sig = NULL;
21 static const uschar * dkim_collect_error = NULL;
22
23 static int
24 dkim_exim_query_dns_txt(char *name, char *answer)
25 {
26 dns_answer dnsa;
27 dns_scan dnss;
28 dns_record *rr;
29
30 lookup_dnssec_authenticated = NULL;
31 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
32   return PDKIM_FAIL;    /*XXX better error detail?  logging? */
33
34 /* Search for TXT record */
35
36 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
37      rr;
38      rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
39   if (rr->type == T_TXT)
40     {
41     int rr_offset = 0;
42     int answer_offset = 0;
43
44     /* Copy record content to the answer buffer */
45
46     while (rr_offset < rr->size)
47       {
48       uschar len = rr->data[rr_offset++];
49       snprintf(answer + answer_offset,
50                 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
51                 "%.*s", (int)len, CS  (rr->data + rr_offset));
52       rr_offset += len;
53       answer_offset += len;
54       if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
55         return PDKIM_FAIL;      /*XXX better error detail?  logging? */
56       }
57     return PDKIM_OK;
58     }
59
60 return PDKIM_FAIL;      /*XXX better error detail?  logging? */
61 }
62
63
64 void
65 dkim_exim_init(void)
66 {
67 pdkim_init();
68 }
69
70
71
72 void
73 dkim_exim_verify_init(BOOL dot_stuffing)
74 {
75 /* There is a store-reset between header & body reception
76 so cannot use the main pool. Any allocs done by Exim
77 memory-handling must use the perm pool. */
78
79 dkim_verify_oldpool = store_pool;
80 store_pool = POOL_PERM;
81
82 /* Free previous context if there is one */
83
84 if (dkim_verify_ctx)
85   pdkim_free_ctx(dkim_verify_ctx);
86
87 /* Create new context */
88
89 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
90 dkim_collect_input = !!dkim_verify_ctx;
91 dkim_collect_error = NULL;
92
93 /* Start feed up with any cached data */
94 receive_get_cache();
95
96 store_pool = dkim_verify_oldpool;
97 }
98
99
100 void
101 dkim_exim_verify_feed(uschar * data, int len)
102 {
103 int rc;
104
105 store_pool = POOL_PERM;
106 if (  dkim_collect_input
107    && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
108   {
109   dkim_collect_error = pdkim_errstr(rc);
110   log_write(0, LOG_MAIN,
111              "DKIM: validation error: %.100s", dkim_collect_error);
112   dkim_collect_input = FALSE;
113   }
114 store_pool = dkim_verify_oldpool;
115 }
116
117
118 void
119 dkim_exim_verify_finish(void)
120 {
121 pdkim_signature * sig = NULL;
122 int dkim_signers_size = 0, dkim_signers_ptr = 0, rc;
123 const uschar * errstr;
124
125 store_pool = POOL_PERM;
126
127 /* Delete eventual previous signature chain */
128
129 dkim_signers = NULL;
130 dkim_signatures = NULL;
131
132 if (dkim_collect_error)
133   {
134   log_write(0, LOG_MAIN,
135       "DKIM: Error during validation, disabling signature verification: %.100s",
136       dkim_collect_error);
137   dkim_disable_verify = TRUE;
138   goto out;
139   }
140
141 dkim_collect_input = FALSE;
142
143 /* Finish DKIM operation and fetch link to signatures chain */
144
145 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
146 if (rc != PDKIM_OK)
147   {
148   log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
149             errstr ? ": " : "", errstr ? errstr : US"");
150   goto out;
151   }
152
153 for (sig = dkim_signatures; sig; sig = sig->next)
154   {
155   int size = 0, ptr = 0;
156   uschar * logmsg = NULL, * s;
157
158   /* Log a line for each signature */
159
160   if (!(s = sig->domain)) s = US"<UNSET>";
161   logmsg = string_append(logmsg, &size, &ptr, 2, "d=", s);
162   if (!(s = sig->selector)) s = US"<UNSET>";
163   logmsg = string_append(logmsg, &size, &ptr, 2, " s=", s);
164   logmsg = string_append(logmsg, &size, &ptr, 7, 
165         " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166         "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
167         " a=", sig->algo == PDKIM_ALGO_RSA_SHA256
168                 ? "rsa-sha256"
169                 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
170         string_sprintf(" b=%d",
171                         (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
172   if ((s= sig->identity)) logmsg = string_append(logmsg, &size, &ptr, 2, " i=", s);
173   if (sig->created > 0) logmsg = string_append(logmsg, &size, &ptr, 1,
174                                       string_sprintf(" t=%lu", sig->created));
175   if (sig->expires > 0) logmsg = string_append(logmsg, &size, &ptr, 1,
176                                       string_sprintf(" x=%lu", sig->expires));
177   if (sig->bodylength > -1) logmsg = string_append(logmsg, &size, &ptr, 1,
178                                       string_sprintf(" l=%lu", sig->bodylength));
179
180   switch (sig->verify_status)
181     {
182     case PDKIM_VERIFY_NONE:
183       logmsg = string_append(logmsg, &size, &ptr, 1, " [not verified]");
184       break;
185
186     case PDKIM_VERIFY_INVALID:
187       logmsg = string_append(logmsg, &size, &ptr, 1, " [invalid - ");
188       switch (sig->verify_ext_status)
189         {
190         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
191           logmsg = string_append(logmsg, &size, &ptr, 1,
192                         "public key record (currently?) unavailable]");
193           break;
194
195         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
196           logmsg = string_append(logmsg, &size, &ptr, 1,
197                         "overlong public key record]");
198           break;
199
200         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
201         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
202           logmsg = string_append(logmsg, &size, &ptr, 1,
203                        "syntax error in public key record]");
204           break;
205
206         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
207           logmsg = string_append(logmsg, &size, &ptr, 1,
208                        "signature tag missing or invalid]");
209           break;
210
211         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
212           logmsg = string_append(logmsg, &size, &ptr, 1,
213                        "unsupported DKIM version]");
214           break;
215
216         default:
217           logmsg = string_append(logmsg, &size, &ptr, 1,
218                         "unspecified problem]");
219         }
220       break;
221
222     case PDKIM_VERIFY_FAIL:
223       logmsg =
224         string_append(logmsg, &size, &ptr, 1, " [verification failed - ");
225       switch (sig->verify_ext_status)
226         {
227         case PDKIM_VERIFY_FAIL_BODY:
228           logmsg = string_append(logmsg, &size, &ptr, 1,
229                        "body hash mismatch (body probably modified in transit)]");
230           break;
231
232         case PDKIM_VERIFY_FAIL_MESSAGE:
233           logmsg = string_append(logmsg, &size, &ptr, 1,
234                        "signature did not verify (headers probably modified in transit)]");
235         break;
236
237         default:
238           logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
239         }
240       break;
241
242     case PDKIM_VERIFY_PASS:
243       logmsg =
244         string_append(logmsg, &size, &ptr, 1, " [verification succeeded]");
245       break;
246     }
247
248   logmsg[ptr] = '\0';
249   log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
250
251   /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
252
253   if (sig->domain)
254     dkim_signers = string_append_listele(dkim_signers, &dkim_signers_size,
255       &dkim_signers_ptr, ':', sig->domain);
256
257   if (sig->identity)
258     dkim_signers = string_append_listele(dkim_signers, &dkim_signers_size,
259       &dkim_signers_ptr, ':', sig->identity);
260
261   /* Process next signature */
262   }
263
264 out:
265 store_pool = dkim_verify_oldpool;
266 }
267
268
269 void
270 dkim_exim_acl_setup(uschar * id)
271 {
272 pdkim_signature * sig;
273 uschar * cmp_val;
274
275 dkim_cur_sig = NULL;
276 dkim_cur_signer = id;
277
278 if (dkim_disable_verify || !id || !dkim_verify_ctx)
279   return;
280
281 /* Find signature to run ACL on */
282
283 for (sig = dkim_signatures; sig; sig = sig->next)
284   if (  (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
285      && strcmpic(cmp_val, id) == 0
286      )
287     {
288     dkim_cur_sig = sig;
289
290     /* The "dkim_domain" and "dkim_selector" expansion variables have
291        related globals, since they are used in the signing code too.
292        Instead of inventing separate names for verification, we set
293        them here. This is easy since a domain and selector is guaranteed
294        to be in a signature. The other dkim_* expansion items are
295        dynamically fetched from dkim_cur_sig at expansion time (see
296        function below). */
297
298     dkim_signing_domain = US sig->domain;
299     dkim_signing_selector = US sig->selector;
300     dkim_key_length = sig->sighash.len * 8;
301     return;
302     }
303 }
304
305
306 static uschar *
307 dkim_exim_expand_defaults(int what)
308 {
309 switch (what)
310   {
311   case DKIM_ALGO:               return US"";
312   case DKIM_BODYLENGTH:         return US"9999999999999";
313   case DKIM_CANON_BODY:         return US"";
314   case DKIM_CANON_HEADERS:      return US"";
315   case DKIM_COPIEDHEADERS:      return US"";
316   case DKIM_CREATED:            return US"0";
317   case DKIM_EXPIRES:            return US"9999999999999";
318   case DKIM_HEADERNAMES:        return US"";
319   case DKIM_IDENTITY:           return US"";
320   case DKIM_KEY_GRANULARITY:    return US"*";
321   case DKIM_KEY_SRVTYPE:        return US"*";
322   case DKIM_KEY_NOTES:          return US"";
323   case DKIM_KEY_TESTING:        return US"0";
324   case DKIM_NOSUBDOMAINS:       return US"0";
325   case DKIM_VERIFY_STATUS:      return US"none";
326   case DKIM_VERIFY_REASON:      return US"";
327   default:                      return US"";
328   }
329 }
330
331
332 uschar *
333 dkim_exim_expand_query(int what)
334 {
335 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
336   return dkim_exim_expand_defaults(what);
337
338 switch (what)
339   {
340   case DKIM_ALGO:
341     switch (dkim_cur_sig->algo)
342       {
343       case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
344       case PDKIM_ALGO_RSA_SHA256:
345       default:                  return US"rsa-sha256";
346       }
347
348   case DKIM_BODYLENGTH:
349     return dkim_cur_sig->bodylength >= 0
350       ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
351       : dkim_exim_expand_defaults(what);
352
353   case DKIM_CANON_BODY:
354     switch (dkim_cur_sig->canon_body)
355       {
356       case PDKIM_CANON_RELAXED: return US"relaxed";
357       case PDKIM_CANON_SIMPLE:
358       default:                  return US"simple";
359       }
360
361   case DKIM_CANON_HEADERS:
362   switch (dkim_cur_sig->canon_headers)
363     {
364     case PDKIM_CANON_RELAXED:   return US"relaxed";
365     case PDKIM_CANON_SIMPLE:
366     default:                    return US"simple";
367     }
368
369   case DKIM_COPIEDHEADERS:
370     return dkim_cur_sig->copiedheaders
371       ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
372
373   case DKIM_CREATED:
374     return dkim_cur_sig->created > 0
375       ? string_sprintf("%llu", dkim_cur_sig->created)
376       : dkim_exim_expand_defaults(what);
377
378   case DKIM_EXPIRES:
379     return dkim_cur_sig->expires > 0
380       ? string_sprintf("%llu", dkim_cur_sig->expires)
381       : dkim_exim_expand_defaults(what);
382
383   case DKIM_HEADERNAMES:
384     return dkim_cur_sig->headernames
385       ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
386
387   case DKIM_IDENTITY:
388     return dkim_cur_sig->identity
389       ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
390
391   case DKIM_KEY_GRANULARITY:
392     return dkim_cur_sig->pubkey
393       ? dkim_cur_sig->pubkey->granularity
394       ? US dkim_cur_sig->pubkey->granularity
395       : dkim_exim_expand_defaults(what)
396       : dkim_exim_expand_defaults(what);
397
398   case DKIM_KEY_SRVTYPE:
399     return dkim_cur_sig->pubkey
400       ? dkim_cur_sig->pubkey->srvtype
401       ? US dkim_cur_sig->pubkey->srvtype
402       : dkim_exim_expand_defaults(what)
403       : dkim_exim_expand_defaults(what);
404
405   case DKIM_KEY_NOTES:
406     return dkim_cur_sig->pubkey
407       ? dkim_cur_sig->pubkey->notes
408       ? US dkim_cur_sig->pubkey->notes
409       : dkim_exim_expand_defaults(what)
410       : dkim_exim_expand_defaults(what);
411
412   case DKIM_KEY_TESTING:
413     return dkim_cur_sig->pubkey
414       ? dkim_cur_sig->pubkey->testing
415       ? US"1"
416       : dkim_exim_expand_defaults(what)
417       : dkim_exim_expand_defaults(what);
418
419   case DKIM_NOSUBDOMAINS:
420     return dkim_cur_sig->pubkey
421       ? dkim_cur_sig->pubkey->no_subdomaining
422       ? US"1"
423       : dkim_exim_expand_defaults(what)
424       : dkim_exim_expand_defaults(what);
425
426   case DKIM_VERIFY_STATUS:
427     switch (dkim_cur_sig->verify_status)
428       {
429       case PDKIM_VERIFY_INVALID:        return US"invalid";
430       case PDKIM_VERIFY_FAIL:           return US"fail";
431       case PDKIM_VERIFY_PASS:           return US"pass";
432       case PDKIM_VERIFY_NONE:
433       default:                          return US"none";
434       }
435
436   case DKIM_VERIFY_REASON:
437     switch (dkim_cur_sig->verify_ext_status)
438       {
439       case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
440                                                 return US"pubkey_unavailable";
441       case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
442       case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:  return US"pubkey_der_syntax";
443       case PDKIM_VERIFY_FAIL_BODY:              return US"bodyhash_mismatch";
444       case PDKIM_VERIFY_FAIL_MESSAGE:           return US"signature_incorrect";
445       }
446
447   default:
448     return US"";
449   }
450 }
451
452
453 /* Generate signatures for the given file, returning a string.
454 If a prefix is given, prepend it to the file for the calculations.
455 */
456
457 uschar *
458 dkim_exim_sign(int fd, off_t off, uschar * prefix,
459   struct ob_dkim * dkim, const uschar ** errstr)
460 {
461 const uschar * dkim_domain;
462 int sep = 0;
463 uschar *seen_items = NULL;
464 int seen_items_size = 0;
465 int seen_items_offset = 0;
466 uschar *dkim_canon_expanded;
467 uschar *dkim_sign_headers_expanded;
468 uschar *dkim_private_key_expanded;
469 pdkim_ctx *ctx = NULL;
470 uschar *rc = NULL;
471 uschar *sigbuf = NULL;
472 int sigsize = 0;
473 int sigptr = 0;
474 pdkim_signature *signature;
475 int pdkim_canon;
476 int pdkim_rc;
477 int sread;
478 uschar buf[4096];
479 int save_errno = 0;
480 int old_pool = store_pool;
481
482 store_pool = POOL_MAIN;
483
484 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
485   {
486   /* expansion error, do not send message. */
487   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
488              "dkim_domain: %s", expand_string_message);
489   goto bad;
490   }
491
492 /* Set $dkim_domain expansion variable to each unique domain in list. */
493
494 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
495   {
496   if (dkim_signing_domain[0] == '\0')
497     continue;
498
499   /* Only sign once for each domain, no matter how often it
500   appears in the expanded list. */
501
502   if (seen_items)
503     {
504     const uschar *seen_items_list = seen_items;
505     if (match_isinlist(dkim_signing_domain,
506                         &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
507                         NULL) == OK)
508       continue;
509
510     seen_items =
511       string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
512     }
513
514   seen_items =
515     string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
516                  dkim_signing_domain);
517   seen_items[seen_items_offset] = '\0';
518
519   /* Set up $dkim_selector expansion variable. */
520
521   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
522     {
523     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
524                "dkim_selector: %s", expand_string_message);
525     goto bad;
526     }
527
528   /* Get canonicalization to use */
529
530   dkim_canon_expanded = dkim->dkim_canon
531     ? expand_string(dkim->dkim_canon) : US"relaxed";
532   if (!dkim_canon_expanded)
533     {
534     /* expansion error, do not send message. */
535     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
536                "dkim_canon: %s", expand_string_message);
537     goto bad;
538     }
539
540   if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
541     pdkim_canon = PDKIM_CANON_RELAXED;
542   else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
543     pdkim_canon = PDKIM_CANON_SIMPLE;
544   else
545     {
546     log_write(0, LOG_MAIN,
547                "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
548                dkim_canon_expanded);
549     pdkim_canon = PDKIM_CANON_RELAXED;
550     }
551
552   dkim_sign_headers_expanded = NULL;
553   if (dkim->dkim_sign_headers)
554     if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
555       {
556       log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
557                  "dkim_sign_headers: %s", expand_string_message);
558       goto bad;
559       }
560                         /* else pass NULL, which means default header list */
561
562   /* Get private key to use. */
563
564   if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
565     {
566     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
567                "dkim_private_key: %s", expand_string_message);
568     goto bad;
569     }
570
571   if (  Ustrlen(dkim_private_key_expanded) == 0
572      || Ustrcmp(dkim_private_key_expanded, "0") == 0
573      || Ustrcmp(dkim_private_key_expanded, "false") == 0
574      )
575     continue;           /* don't sign, but no error */
576
577   if (dkim_private_key_expanded[0] == '/')
578     {
579     int privkey_fd, off = 0, len;
580
581     /* Looks like a filename, load the private key. */
582
583     memset(big_buffer, 0, big_buffer_size);
584
585     if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
586       {
587       log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
588                  "private key file for reading: %s",
589                  dkim_private_key_expanded);
590       goto bad;
591       }
592
593     do
594       {
595       if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
596         {
597         (void) close(privkey_fd);
598         log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
599                    dkim_private_key_expanded);
600         goto bad;
601         }
602       off += len;
603       }
604     while (len > 0);
605
606     (void) close(privkey_fd);
607     big_buffer[off] = '\0';
608     dkim_private_key_expanded = big_buffer;
609     }
610
611   if (!(ctx = pdkim_init_sign(CS dkim_signing_domain,
612                         CS dkim_signing_selector,
613                         CS dkim_private_key_expanded,
614                         PDKIM_ALGO_RSA_SHA256,
615                         dkim->dot_stuffed,
616                         &dkim_exim_query_dns_txt,
617                         errstr
618                         )))
619     goto bad;
620   dkim_private_key_expanded[0] = '\0';
621   pdkim_set_optional(ctx,
622                       CS dkim_sign_headers_expanded,
623                       NULL,
624                       pdkim_canon,
625                       pdkim_canon, -1, 0, 0);
626
627   if (prefix)
628     pdkim_feed(ctx, prefix, Ustrlen(prefix));
629
630   if (lseek(fd, off, SEEK_SET) < 0)
631     sread = -1;
632   else
633     while ((sread = read(fd, &buf, sizeof(buf))) > 0)
634       if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
635         goto pk_bad;
636
637   /* Handle failed read above. */
638   if (sread == -1)
639     {
640     debug_printf("DKIM: Error reading -K file.\n");
641     save_errno = errno;
642     goto bad;
643     }
644
645   if ((pdkim_rc = pdkim_feed_finish(ctx, &signature, errstr)) != PDKIM_OK)
646     goto pk_bad;
647
648   sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
649                           US signature->signature_header, US"\r\n");
650
651   pdkim_free_ctx(ctx);
652   ctx = NULL;
653   }
654
655 if (sigbuf)
656   {
657   sigbuf[sigptr] = '\0';
658   rc = sigbuf;
659   }
660 else
661   rc = US"";
662
663 CLEANUP:
664   if (ctx)
665     pdkim_free_ctx(ctx);
666   store_pool = old_pool;
667   errno = save_errno;
668   return rc;
669
670 pk_bad:
671   log_write(0, LOG_MAIN|LOG_PANIC,
672                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
673 bad:
674   rc = NULL;
675   goto CLEANUP;
676 }
677
678 #endif