More debug for fail cases in kevent set-watch
[exim.git] / src / src / tls.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is
10 based on a patch that was originally contributed by Steve Haslam. It was
11 adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
12 based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
13 are so very different, the functions for each are kept in separate files. The
14 relevant file is #included as required, after any any common functions.
15
16 No cryptographic code is included in Exim. All this module does is to call
17 functions from the OpenSSL or GNU TLS libraries. */
18
19
20 #include "exim.h"
21 #include "transports/smtp.h"
22
23 #if !defined(DISABLE_TLS) && !defined(USE_OPENSSL) && !defined(USE_GNUTLS)
24 # error One of USE_OPENSSL or USE_GNUTLS must be defined for a TLS build
25 #endif
26
27
28 #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
29 # include "macro_predef.h"
30 # ifdef USE_GNUTLS
31 #  include "tls-gnu.c"
32 # else
33 #  include "tls-openssl.c"
34 # endif
35 #endif
36
37 #ifndef MACRO_PREDEF
38
39 static void tls_per_lib_daemon_init(void);
40 static void tls_per_lib_daemon_tick(void);
41 static void tls_server_creds_init(void);
42 static void tls_server_creds_invalidate(void);
43 static void tls_client_creds_init(transport_instance *, BOOL);
44 static void tls_client_creds_invalidate(transport_instance *);
45 static void tls_daemon_creds_reload(void);
46 static BOOL opt_set_and_noexpand(const uschar *);
47 static BOOL opt_unset_or_noexpand(const uschar *);
48
49
50
51 /* This module is compiled only when it is specifically requested in the
52 build-time configuration. However, some compilers don't like compiling empty
53 modules, so keep them happy with a dummy when skipping the rest. Make it
54 reference itself to stop picky compilers complaining that it is unused, and put
55 in a dummy argument to stop even pickier compilers complaining about infinite
56 loops. */
57
58 #ifdef DISABLE_TLS
59 static void dummy(int x) { dummy(x-1); }
60 #else   /* most of the rest of the file */
61
62 const exim_tlslib_state null_tls_preload = {0};
63
64 /* Static variables that are used for buffering data by both sets of
65 functions and the common functions below.
66
67 We're moving away from this; GnuTLS is already using a state, which
68 can switch, so we can do TLS callouts during ACLs. */
69
70 static const int ssl_xfer_buffer_size = 4096;
71 #ifdef USE_OPENSSL
72 static uschar *ssl_xfer_buffer = NULL;
73 static int ssl_xfer_buffer_lwm = 0;
74 static int ssl_xfer_buffer_hwm = 0;
75 static int ssl_xfer_eof = FALSE;
76 static BOOL ssl_xfer_error = FALSE;
77 #endif
78
79 #ifdef EXIM_HAVE_KEVENT
80 # define KEV_SIZE 16    /* Eight file,dir pairs */
81 static struct kevent kev[KEV_SIZE];
82 static int kev_used = 0;
83 #endif
84
85 /*************************************************
86 *       Expand string; give error on failure     *
87 *************************************************/
88
89 /* If expansion is forced to fail, set the result NULL and return TRUE.
90 Other failures return FALSE. For a server, an SMTP response is given.
91
92 Arguments:
93   s         the string to expand; if NULL just return TRUE
94   name      name of string being expanded (for error)
95   result    where to put the result
96
97 Returns:    TRUE if OK; result may still be NULL after forced failure
98 */
99
100 static BOOL
101 expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
102 {
103 if (!s)
104   *result = NULL;
105 else if (  !(*result = expand_string(US s)) /* need to clean up const more */
106         && !f.expand_string_forcedfail
107         )
108   {
109   *errstr = US"Internal error";
110   log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
111     expand_string_message);
112   return FALSE;
113   }
114 return TRUE;
115 }
116
117
118 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
119 /* Add the directory for a filename to the inotify handle, creating that if
120 needed.  This is enough to see changes to files in that dir.
121 Return boolean success.
122
123 The word "system" fails, which is on the safe side as we don't know what
124 directory it implies nor if the TLS library handles a watch for us.
125
126 The string "system,cache" is recognised and explicitly accepted without
127 setting a watch.  This permits the system CA bundle to be cached even though
128 we have no way to tell when it gets modified by an update.
129 The call chain for OpenSSL uses a (undocumented) call into the library
130 to discover the actual file.  We don't know what GnuTLS uses.
131
132 A full set of caching including the CAs takes 35ms output off of the
133 server tls_init() (GnuTLS, Fedora 32, 2018-class x86_64 laptop hardware).
134 */
135 static BOOL
136 tls_set_one_watch(const uschar * filename)
137 # ifdef EXIM_HAVE_INOTIFY
138 {
139 uschar * s;
140
141 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
142
143 if (!(s = Ustrrchr(filename, '/'))) return FALSE;
144 s = string_copyn(filename, s - filename);       /* mem released by tls_set_watch */
145 DEBUG(D_tls) debug_printf("watch dir '%s'\n", s);
146
147 /*XXX unclear what effect symlinked files will have for inotify */
148
149 if (inotify_add_watch(tls_watch_fd, CCS s,
150       IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF
151       | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF) >= 0)
152   return TRUE;
153 DEBUG(D_tls) debug_printf("notify_add_watch: %s\n", strerror(errno));
154 return FALSE;
155 }
156 # endif
157 # ifdef EXIM_HAVE_KEVENT
158 {
159 uschar * s;
160 int fd1, fd2, i, cnt = 0;
161 struct stat sb;
162
163 errno = 0;
164 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
165
166 for (;;)
167   {
168   if (!(s = Ustrrchr(filename, '/'))) return FALSE;
169   if ((lstat(CCS filename, &sb)) < 0) { s = US"lstat"; goto bad; }
170   if (kev_used > KEV_SIZE-2) { s = US"out of kev space"; goto bad; }
171
172   /* The dir open will fail if there is a symlink on the path. Fine; it's too
173   much effort to handle all possible cases; just refuse the preload. */
174
175   if ((fd2 = open(CCS s, O_RDONLY | O_NOFOLLOW)) < 0) { s = US"open dir"; goto bad; }
176
177   if (!S_ISLNK(sb.st_mode))
178     {
179     if ((fd1 = open(CCS filename, O_RDONLY | O_NOFOLLOW)) < 0)
180       { s = US"open file"; goto bad; }
181     DEBUG(D_tls) debug_printf("watch file '%s'\n", filename);
182     EV_SET(&kev[++kev_used],
183         (uintptr_t)fd1,
184         EVFILT_VNODE,
185         EV_ADD | EV_ENABLE | EV_ONESHOT,
186         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
187         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
188         0,
189         NULL);
190     cnt++;
191     }
192   DEBUG(D_tls) debug_printf("watch dir  '%s'\n", s);
193   EV_SET(&kev[++kev_used],
194         (uintptr_t)fd2,
195         EVFILT_VNODE,
196         EV_ADD | EV_ENABLE | EV_ONESHOT,
197         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
198         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
199         0,
200         NULL);
201   cnt++;
202
203   if (!(S_ISLNK(sb.st_mode))) break;
204
205   s = store_get(1024, FALSE);
206   if ((i = readlink(CCS filename, s, 1024)) < 0) { s = US"readlink"; goto bad; }
207   filename = s;
208   *(s += i) = '\0';
209   store_release_above(s+1);
210   }
211
212 if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt, NULL, 0, NULL) >= 0)
213   return TRUE;
214 s = US"kevent";
215
216 bad:
217 DEBUG(D_tls)
218   if (errno)
219     debug_printf("%s: %s: %s\n", __FUNCTION__, s, strerror(errno));
220   else
221     debug_printf("%s: %s\n", __FUNCTION__, s);
222 return FALSE;
223 }
224 # endif /*EXIM_HAVE_KEVENT*/
225
226
227 /* Create an inotify facility if needed.
228 Then set watches on the dir containing the given file or (optionally)
229 list of files.  Return boolean success. */
230
231 static BOOL
232 tls_set_watch(const uschar * filename, BOOL list)
233 {
234 rmark r;
235 BOOL rc = FALSE;
236
237 if (!filename || !*filename) return TRUE;
238 if (Ustrncmp(filename, "system", 6) == 0) return TRUE;
239
240 DEBUG(D_tls) debug_printf("tls_set_watch: '%s'\n", filename);
241
242 if (  tls_watch_fd < 0
243 # ifdef EXIM_HAVE_INOTIFY
244    && (tls_watch_fd = inotify_init1(O_CLOEXEC)) < 0
245 # endif
246 # ifdef EXIM_HAVE_KEVENT
247    && (tls_watch_fd = kqueue()) < 0
248 # endif
249    )
250     {
251     DEBUG(D_tls) debug_printf("inotify_init: %s\n", strerror(errno));
252     return FALSE;
253     }
254
255 r = store_mark();
256
257 if (list)
258   {
259   int sep = 0;
260   for (uschar * s; s = string_nextinlist(&filename, &sep, NULL, 0); )
261     if (!(rc = tls_set_one_watch(s))) break;
262   }
263 else
264   rc = tls_set_one_watch(filename);
265
266 store_reset(r);
267 if (!rc) DEBUG(D_tls) debug_printf("tls_set_watch() fail on '%s': %s\n", filename, strerror(errno));
268 return rc;
269 }
270
271
272 void
273 tls_watch_discard_event(int fd)
274 {
275 #ifdef EXIM_HAVE_INOTIFY
276 (void) read(fd, big_buffer, big_buffer_size);
277 #endif
278 #ifdef EXIM_HAVE_KEVENT
279 struct kevent kev;
280 struct timespec t = {0};
281 (void) kevent(fd, NULL, 0, &kev, 1, &t);
282 #endif
283 }
284
285 /* Called, after a delay for multiple file ops to get done, from
286 the daemon when any of the watches added (above) fire.
287
288 Dump the set of watches and arrange to reload cached creds (which
289 will set up new watches). */
290
291 static void
292 tls_watch_triggered(void)
293 {
294 DEBUG(D_tls) debug_printf("watch triggered\n");
295
296 tls_daemon_creds_reload();
297 }
298 #endif  /*EXIM_HAVE_INOTIFY*/
299
300
301 void
302 tls_client_creds_reload(BOOL watch)
303 {
304 for(transport_instance * t = transports; t; t = t->next)
305   if (Ustrcmp(t->driver_name, "smtp") == 0)
306     {
307     tls_client_creds_invalidate(t);
308     tls_client_creds_init(t, watch);
309     }
310 }
311
312
313 void
314 tls_watch_invalidate(void)
315 {
316 if (tls_watch_fd < 0) return;
317
318 #ifdef EXIM_HAVE_KEVENT
319 /* Close the files we had open for kevent */
320 for (int fd, i = 0; i < kev_used; i++)
321   {
322   (void) close((int) kev[i].ident);
323   kev[i].ident = (uintptr_t)-1;
324   }
325 kev_used = 0;
326 #endif
327
328 close(tls_watch_fd);
329 tls_watch_fd = -1;
330 }
331
332
333 static void
334 tls_daemon_creds_reload(void)
335 {
336 #ifdef EXIM_HAVE_KEVENT
337 tls_watch_invalidate();
338 #endif
339
340 tls_server_creds_invalidate();
341 tls_server_creds_init();
342
343 tls_client_creds_reload(TRUE);
344 }
345
346
347 /* Utility predicates for use by the per-library code */
348 static BOOL
349 opt_set_and_noexpand(const uschar * opt)
350 { return opt && *opt && Ustrchr(opt, '$') == NULL; }
351
352 static BOOL
353 opt_unset_or_noexpand(const uschar * opt)
354 { return !opt || Ustrchr(opt, '$') == NULL; }
355
356
357
358 /* Called every time round the daemon loop */
359
360 void
361 tls_daemon_tick(void)
362 {
363 tls_per_lib_daemon_tick();
364 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
365 if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
366   {
367   tls_watch_trigger_time = 0;
368   tls_watch_triggered();
369   }
370 #endif
371 }
372
373 /* Called once at daemon startup */
374
375 void
376 tls_daemon_init(void)
377 {
378 tls_per_lib_daemon_init();
379 }
380
381
382 /*************************************************
383 *        Timezone environment flipping           *
384 *************************************************/
385
386 static uschar *
387 to_tz(uschar * tz)
388 {
389 uschar * old = US getenv("TZ");
390 (void) setenv("TZ", CCS tz, 1);
391 tzset();
392 return old;
393 }
394
395 static void
396 restore_tz(uschar * tz)
397 {
398 if (tz)
399   (void) setenv("TZ", CCS tz, 1);
400 else
401   (void) os_unsetenv(US"TZ");
402 tzset();
403 }
404
405 /*************************************************
406 *        Many functions are package-specific     *
407 *************************************************/
408
409 #ifdef USE_GNUTLS
410 # include "tls-gnu.c"
411 # include "tlscert-gnu.c"
412 # define ssl_xfer_buffer (state_server.xfer_buffer)
413 # define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
414 # define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
415 # define ssl_xfer_eof (state_server.xfer_eof)
416 # define ssl_xfer_error (state_server.xfer_error)
417 #endif
418
419 #ifdef USE_OPENSSL
420 # include "tls-openssl.c"
421 # include "tlscert-openssl.c"
422 #endif
423
424
425
426 /*************************************************
427 *           TLS version of ungetc                *
428 *************************************************/
429
430 /* Puts a character back in the input buffer. Only ever
431 called once.
432 Only used by the server-side TLS.
433
434 Arguments:
435   ch           the character
436
437 Returns:       the character
438 */
439
440 int
441 tls_ungetc(int ch)
442 {
443 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
444 return ch;
445 }
446
447
448
449 /*************************************************
450 *           TLS version of feof                  *
451 *************************************************/
452
453 /* Tests for a previous EOF
454 Only used by the server-side TLS.
455
456 Arguments:     none
457 Returns:       non-zero if the eof flag is set
458 */
459
460 int
461 tls_feof(void)
462 {
463 return (int)ssl_xfer_eof;
464 }
465
466
467
468 /*************************************************
469 *              TLS version of ferror             *
470 *************************************************/
471
472 /* Tests for a previous read error, and returns with errno
473 restored to what it was when the error was detected.
474 Only used by the server-side TLS.
475
476 >>>>> Hmm. Errno not handled yet. Where do we get it from?  >>>>>
477
478 Arguments:     none
479 Returns:       non-zero if the error flag is set
480 */
481
482 int
483 tls_ferror(void)
484 {
485 return (int)ssl_xfer_error;
486 }
487
488
489 /*************************************************
490 *           TLS version of smtp_buffered         *
491 *************************************************/
492
493 /* Tests for unused chars in the TLS input buffer.
494 Only used by the server-side TLS.
495
496 Arguments:     none
497 Returns:       TRUE/FALSE
498 */
499
500 BOOL
501 tls_smtp_buffered(void)
502 {
503 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
504 }
505
506
507 #endif  /*DISABLE_TLS*/
508
509 void
510 tls_modify_variables(tls_support * dest_tsp)
511 {
512 modify_variable(US"tls_bits",                 &dest_tsp->bits);
513 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
514 modify_variable(US"tls_cipher",               &dest_tsp->cipher);
515 modify_variable(US"tls_peerdn",               &dest_tsp->peerdn);
516 #ifdef USE_OPENSSL
517 modify_variable(US"tls_sni",                  &dest_tsp->sni);
518 #endif
519 }
520
521
522 #ifndef DISABLE_TLS
523 /************************************************
524 *       TLS certificate name operations         *
525 ************************************************/
526
527 /* Convert an rfc4514 DN to an exim comma-sep list.
528 Backslashed commas need to be replaced by doublecomma
529 for Exim's list quoting.  We modify the given string
530 inplace.
531 */
532
533 static void
534 dn_to_list(uschar * dn)
535 {
536 for (uschar * cp = dn; *cp; cp++)
537   if (cp[0] == '\\' && cp[1] == ',')
538     *cp++ = ',';
539 }
540
541
542 /* Extract fields of a given type from an RFC4514-
543 format Distinguished Name.  Return an Exim list.
544 NOTE: We modify the supplied dn string during operation.
545
546 Arguments:
547         dn      Distinguished Name string
548         mod     list containing optional output list-sep and
549                 field selector match, comma-separated
550 Return:
551         allocated string with list of matching fields,
552         field type stripped
553 */
554
555 uschar *
556 tls_field_from_dn(uschar * dn, const uschar * mod)
557 {
558 int insep = ',';
559 uschar outsep = '\n';
560 uschar * ele;
561 uschar * match = NULL;
562 int len;
563 gstring * list = NULL;
564
565 while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
566   if (ele[0] != '>')
567     match = ele;        /* field tag to match */
568   else if (ele[1])
569     outsep = ele[1];    /* nondefault output separator */
570
571 dn_to_list(dn);
572 insep = ',';
573 len = match ? Ustrlen(match) : -1;
574 while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
575   if (  !match
576      || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
577      )
578     list = string_append_listele(list, outsep, ele+len+1);
579 return string_from_gstring(list);
580 }
581
582
583 /* Compare a domain name with a possibly-wildcarded name. Wildcards
584 are restricted to a single one, as the first element of patterns
585 having at least three dot-separated elements.  Case-independent.
586 Return TRUE for a match
587 */
588 static BOOL
589 is_name_match(const uschar * name, const uschar * pat)
590 {
591 uschar * cp;
592 return *pat == '*'              /* possible wildcard match */
593   ?    *++pat == '.'            /* starts star, dot              */
594     && !Ustrchr(++pat, '*')     /* has no more stars             */
595     && Ustrchr(pat, '.')        /* and has another dot.          */
596     && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
597     && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
598   :    !Ustrchr(pat+1, '*')
599     && strcmpic(name, pat) == 0;
600 }
601
602 /* Compare a list of names with the dnsname elements
603 of the Subject Alternate Name, if any, and the
604 Subject otherwise.
605
606 Arguments:
607         namelist names to compare
608         cert     certificate
609
610 Returns:
611         TRUE/FALSE
612 */
613
614 BOOL
615 tls_is_name_for_cert(const uschar * namelist, void * cert)
616 {
617 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
618 uschar * subjdn;
619 uschar * certname;
620 int cmp_sep = 0;
621 uschar * cmpname;
622
623 if ((altnames = tls_cert_subject_altname(cert, US"dns")))
624   {
625   int alt_sep = '\n';
626   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
627     {
628     const uschar * an = altnames;
629     while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
630       if (is_name_match(cmpname, certname))
631         return TRUE;
632     }
633   }
634
635 else if ((subjdn = tls_cert_subject(cert, NULL)))
636   {
637   int sn_sep = ',';
638
639   dn_to_list(subjdn);
640   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
641     {
642     const uschar * sn = subjdn;
643     while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
644       if (  *certname++ == 'C'
645          && *certname++ == 'N'
646          && *certname++ == '='
647          && is_name_match(cmpname, certname)
648          )
649         return TRUE;
650     }
651   }
652 return FALSE;
653 }
654
655
656 /* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
657 and writes a file by that name.  Our OpenSSL code does the same, using keying
658 info from the library API.
659 The GnuTLS support only works if exim is run by root, not taking advantage of
660 the setuid bit.
661 You can use either the external environment (modulo the keep_environment config)
662 or the add_environment config option for SSLKEYLOGFILE; the latter takes
663 precedence.
664
665 If the path is absolute, require it starts with the spooldir; otherwise delete
666 the env variable.  If relative, prefix the spooldir.
667 */
668 void
669 tls_clean_env(void)
670 {
671 uschar * path = US getenv("SSLKEYLOGFILE");
672 if (path)
673   if (!*path)
674     unsetenv("SSLKEYLOGFILE");
675   else if (*path != '/')
676     {
677     DEBUG(D_tls)
678       debug_printf("prepending spooldir to  env SSLKEYLOGFILE\n");
679     setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
680     }
681   else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
682     {
683     DEBUG(D_tls)
684       debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
685     unsetenv("SSLKEYLOGFILE");
686     }
687 }
688
689 /*************************************************
690 *       Drop privs for checking TLS config      *
691 *************************************************/
692
693 /* We want to validate TLS options during readconf, but do not want to be
694 root when we call into the TLS library, in case of library linkage errors
695 which cause segfaults; before this check, those were always done as the Exim
696 runtime user and it makes sense to continue with that.
697
698 Assumes:  tls_require_ciphers has been set, if it will be
699           exim_user has been set, if it will be
700           exim_group has been set, if it will be
701
702 Returns:  bool for "okay"; false will cause caller to immediately exit.
703 */
704
705 BOOL
706 tls_dropprivs_validate_require_cipher(BOOL nowarn)
707 {
708 const uschar *errmsg;
709 pid_t pid;
710 int rc, status;
711 void (*oldsignal)(int);
712
713 /* If TLS will never be used, no point checking ciphers */
714
715 if (  !tls_advertise_hosts
716    || !*tls_advertise_hosts
717    || Ustrcmp(tls_advertise_hosts, ":") == 0
718    )
719   return TRUE;
720 else if (!nowarn && !tls_certificate)
721   log_write(0, LOG_MAIN,
722     "Warning: No server certificate defined; will use a selfsigned one.\n"
723     " Suggested action: either install a certificate or change tls_advertise_hosts option");
724
725 oldsignal = signal(SIGCHLD, SIG_DFL);
726
727 fflush(NULL);
728 if ((pid = exim_fork(US"cipher-validate")) < 0)
729   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
730
731 if (pid == 0)
732   {
733   /* in some modes, will have dropped privilege already */
734   if (!geteuid())
735     exim_setugid(exim_uid, exim_gid, FALSE,
736         US"calling tls_validate_require_cipher");
737
738   if ((errmsg = tls_validate_require_cipher()))
739     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
740         "tls_require_ciphers invalid: %s", errmsg);
741   fflush(NULL);
742   exim_underbar_exit(EXIT_SUCCESS);
743   }
744
745 do {
746   rc = waitpid(pid, &status, 0);
747 } while (rc < 0 && errno == EINTR);
748
749 DEBUG(D_tls)
750   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
751       (int)pid, status);
752
753 signal(SIGCHLD, oldsignal);
754
755 return status == 0;
756 }
757
758
759
760
761 #endif  /*!DISABLE_TLS*/
762 #endif  /*!MACRO_PREDEF*/
763
764 /* vi: aw ai sw=2
765 */
766 /* End of tls.c */