kevent: handle OpenBSD API anomaly
[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 #ifdef OpenBSD
163 struct kevent k_dummy;
164 #endif
165
166 errno = 0;
167 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
168
169 for (;;)
170   {
171   if (kev_used > KEV_SIZE-2) { s = US"out of kev space"; goto bad; }
172   if (!(s = Ustrrchr(filename, '/'))) return FALSE;
173   s = string_copyn(filename, s - filename);     /* mem released by tls_set_watch */
174
175   /* The dir open will fail if there is a symlink on the path. Fine; it's too
176   much effort to handle all possible cases; just refuse the preload. */
177
178   if ((fd2 = open(CCS s, O_RDONLY | O_NOFOLLOW)) < 0) { s = US"open dir"; goto bad; }
179
180   if ((lstat(CCS filename, &sb)) < 0) { s = US"lstat"; goto bad; }
181   if (!S_ISLNK(sb.st_mode))
182     {
183     if ((fd1 = open(CCS filename, O_RDONLY | O_NOFOLLOW)) < 0)
184       { s = US"open file"; goto bad; }
185     DEBUG(D_tls) debug_printf("watch file '%s'\n", filename);
186     EV_SET(&kev[++kev_used],
187         (uintptr_t)fd1,
188         EVFILT_VNODE,
189         EV_ADD | EV_ENABLE | EV_ONESHOT,
190         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
191         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
192         0,
193         NULL);
194     cnt++;
195     }
196   DEBUG(D_tls) debug_printf("watch dir  '%s'\n", s);
197   EV_SET(&kev[++kev_used],
198         (uintptr_t)fd2,
199         EVFILT_VNODE,
200         EV_ADD | EV_ENABLE | EV_ONESHOT,
201         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
202         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
203         0,
204         NULL);
205   cnt++;
206
207   if (!(S_ISLNK(sb.st_mode))) break;
208
209   s = store_get(1024, FALSE);
210   if ((i = readlink(CCS filename, (void *)s, 1024)) < 0) { s = US"readlink"; goto bad; }
211   filename = s;
212   *(s += i) = '\0';
213   store_release_above(s+1);
214   }
215
216 if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt,
217 #ifdef OpenBSD
218             &k_dummy, 1,
219 #else
220             NULL, 0,
221 #endif
222             NULL) >= 0)
223   return TRUE;
224 s = US"kevent";
225
226 bad:
227 DEBUG(D_tls)
228   if (errno)
229     debug_printf("%s: %s: %s\n", __FUNCTION__, s, strerror(errno));
230   else
231     debug_printf("%s: %s\n", __FUNCTION__, s);
232 return FALSE;
233 }
234 # endif /*EXIM_HAVE_KEVENT*/
235
236
237 /* Create an inotify facility if needed.
238 Then set watches on the dir containing the given file or (optionally)
239 list of files.  Return boolean success. */
240
241 static BOOL
242 tls_set_watch(const uschar * filename, BOOL list)
243 {
244 rmark r;
245 BOOL rc = FALSE;
246
247 if (!filename || !*filename) return TRUE;
248 if (Ustrncmp(filename, "system", 6) == 0) return TRUE;
249
250 DEBUG(D_tls) debug_printf("tls_set_watch: '%s'\n", filename);
251
252 if (  tls_watch_fd < 0
253 # ifdef EXIM_HAVE_INOTIFY
254    && (tls_watch_fd = inotify_init1(O_CLOEXEC)) < 0
255 # endif
256 # ifdef EXIM_HAVE_KEVENT
257    && (tls_watch_fd = kqueue()) < 0
258 # endif
259    )
260     {
261     DEBUG(D_tls) debug_printf("inotify_init: %s\n", strerror(errno));
262     return FALSE;
263     }
264
265 r = store_mark();
266
267 if (list)
268   {
269   int sep = 0;
270   for (uschar * s; s = string_nextinlist(&filename, &sep, NULL, 0); )
271     if (!(rc = tls_set_one_watch(s))) break;
272   }
273 else
274   rc = tls_set_one_watch(filename);
275
276 store_reset(r);
277 if (!rc) DEBUG(D_tls) debug_printf("tls_set_watch() fail on '%s': %s\n", filename, strerror(errno));
278 return rc;
279 }
280
281
282 void
283 tls_watch_discard_event(int fd)
284 {
285 #ifdef EXIM_HAVE_INOTIFY
286 (void) read(fd, big_buffer, big_buffer_size);
287 #endif
288 #ifdef EXIM_HAVE_KEVENT
289 struct kevent kev;
290 struct timespec t = {0};
291 (void) kevent(fd, NULL, 0, &kev, 1, &t);
292 #endif
293 }
294
295 /* Called, after a delay for multiple file ops to get done, from
296 the daemon when any of the watches added (above) fire.
297
298 Dump the set of watches and arrange to reload cached creds (which
299 will set up new watches). */
300
301 static void
302 tls_watch_triggered(void)
303 {
304 DEBUG(D_tls) debug_printf("watch triggered\n");
305
306 tls_daemon_creds_reload();
307 }
308 #endif  /*EXIM_HAVE_INOTIFY*/
309
310
311 void
312 tls_client_creds_reload(BOOL watch)
313 {
314 for(transport_instance * t = transports; t; t = t->next)
315   if (Ustrcmp(t->driver_name, "smtp") == 0)
316     {
317     tls_client_creds_invalidate(t);
318     tls_client_creds_init(t, watch);
319     }
320 }
321
322
323 void
324 tls_watch_invalidate(void)
325 {
326 if (tls_watch_fd < 0) return;
327
328 #ifdef EXIM_HAVE_KEVENT
329 /* Close the files we had open for kevent */
330 for (int i = 0; i < kev_used; i++)
331   {
332   (void) close((int) kev[i].ident);
333   kev[i].ident = (uintptr_t)-1;
334   }
335 kev_used = 0;
336 #endif
337
338 close(tls_watch_fd);
339 tls_watch_fd = -1;
340 }
341
342
343 static void
344 tls_daemon_creds_reload(void)
345 {
346 #ifdef EXIM_HAVE_KEVENT
347 tls_watch_invalidate();
348 #endif
349
350 tls_server_creds_invalidate();
351 tls_server_creds_init();
352
353 tls_client_creds_reload(TRUE);
354 }
355
356
357 /* Utility predicates for use by the per-library code */
358 static BOOL
359 opt_set_and_noexpand(const uschar * opt)
360 { return opt && *opt && Ustrchr(opt, '$') == NULL; }
361
362 static BOOL
363 opt_unset_or_noexpand(const uschar * opt)
364 { return !opt || Ustrchr(opt, '$') == NULL; }
365
366
367
368 /* Called every time round the daemon loop */
369
370 void
371 tls_daemon_tick(void)
372 {
373 tls_per_lib_daemon_tick();
374 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
375 if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
376   {
377   tls_watch_trigger_time = 0;
378   tls_watch_triggered();
379   }
380 #endif
381 }
382
383 /* Called once at daemon startup */
384
385 void
386 tls_daemon_init(void)
387 {
388 tls_per_lib_daemon_init();
389 }
390
391
392 /*************************************************
393 *        Timezone environment flipping           *
394 *************************************************/
395
396 static uschar *
397 to_tz(uschar * tz)
398 {
399 uschar * old = US getenv("TZ");
400 (void) setenv("TZ", CCS tz, 1);
401 tzset();
402 return old;
403 }
404
405 static void
406 restore_tz(uschar * tz)
407 {
408 if (tz)
409   (void) setenv("TZ", CCS tz, 1);
410 else
411   (void) os_unsetenv(US"TZ");
412 tzset();
413 }
414
415 /*************************************************
416 *        Many functions are package-specific     *
417 *************************************************/
418
419 #ifdef USE_GNUTLS
420 # include "tls-gnu.c"
421 # include "tlscert-gnu.c"
422 # define ssl_xfer_buffer (state_server.xfer_buffer)
423 # define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
424 # define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
425 # define ssl_xfer_eof (state_server.xfer_eof)
426 # define ssl_xfer_error (state_server.xfer_error)
427 #endif
428
429 #ifdef USE_OPENSSL
430 # include "tls-openssl.c"
431 # include "tlscert-openssl.c"
432 #endif
433
434
435
436 /*************************************************
437 *           TLS version of ungetc                *
438 *************************************************/
439
440 /* Puts a character back in the input buffer. Only ever
441 called once.
442 Only used by the server-side TLS.
443
444 Arguments:
445   ch           the character
446
447 Returns:       the character
448 */
449
450 int
451 tls_ungetc(int ch)
452 {
453 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
454 return ch;
455 }
456
457
458
459 /*************************************************
460 *           TLS version of feof                  *
461 *************************************************/
462
463 /* Tests for a previous EOF
464 Only used by the server-side TLS.
465
466 Arguments:     none
467 Returns:       non-zero if the eof flag is set
468 */
469
470 int
471 tls_feof(void)
472 {
473 return (int)ssl_xfer_eof;
474 }
475
476
477
478 /*************************************************
479 *              TLS version of ferror             *
480 *************************************************/
481
482 /* Tests for a previous read error, and returns with errno
483 restored to what it was when the error was detected.
484 Only used by the server-side TLS.
485
486 >>>>> Hmm. Errno not handled yet. Where do we get it from?  >>>>>
487
488 Arguments:     none
489 Returns:       non-zero if the error flag is set
490 */
491
492 int
493 tls_ferror(void)
494 {
495 return (int)ssl_xfer_error;
496 }
497
498
499 /*************************************************
500 *           TLS version of smtp_buffered         *
501 *************************************************/
502
503 /* Tests for unused chars in the TLS input buffer.
504 Only used by the server-side TLS.
505
506 Arguments:     none
507 Returns:       TRUE/FALSE
508 */
509
510 BOOL
511 tls_smtp_buffered(void)
512 {
513 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
514 }
515
516
517 #endif  /*DISABLE_TLS*/
518
519 void
520 tls_modify_variables(tls_support * dest_tsp)
521 {
522 modify_variable(US"tls_bits",                 &dest_tsp->bits);
523 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
524 modify_variable(US"tls_cipher",               &dest_tsp->cipher);
525 modify_variable(US"tls_peerdn",               &dest_tsp->peerdn);
526 #ifdef USE_OPENSSL
527 modify_variable(US"tls_sni",                  &dest_tsp->sni);
528 #endif
529 }
530
531
532 #ifndef DISABLE_TLS
533 /************************************************
534 *       TLS certificate name operations         *
535 ************************************************/
536
537 /* Convert an rfc4514 DN to an exim comma-sep list.
538 Backslashed commas need to be replaced by doublecomma
539 for Exim's list quoting.  We modify the given string
540 inplace.
541 */
542
543 static void
544 dn_to_list(uschar * dn)
545 {
546 for (uschar * cp = dn; *cp; cp++)
547   if (cp[0] == '\\' && cp[1] == ',')
548     *cp++ = ',';
549 }
550
551
552 /* Extract fields of a given type from an RFC4514-
553 format Distinguished Name.  Return an Exim list.
554 NOTE: We modify the supplied dn string during operation.
555
556 Arguments:
557         dn      Distinguished Name string
558         mod     list containing optional output list-sep and
559                 field selector match, comma-separated
560 Return:
561         allocated string with list of matching fields,
562         field type stripped
563 */
564
565 uschar *
566 tls_field_from_dn(uschar * dn, const uschar * mod)
567 {
568 int insep = ',';
569 uschar outsep = '\n';
570 uschar * ele;
571 uschar * match = NULL;
572 int len;
573 gstring * list = NULL;
574
575 while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
576   if (ele[0] != '>')
577     match = ele;        /* field tag to match */
578   else if (ele[1])
579     outsep = ele[1];    /* nondefault output separator */
580
581 dn_to_list(dn);
582 insep = ',';
583 len = match ? Ustrlen(match) : -1;
584 while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
585   if (  !match
586      || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
587      )
588     list = string_append_listele(list, outsep, ele+len+1);
589 return string_from_gstring(list);
590 }
591
592
593 /* Compare a domain name with a possibly-wildcarded name. Wildcards
594 are restricted to a single one, as the first element of patterns
595 having at least three dot-separated elements.  Case-independent.
596 Return TRUE for a match
597 */
598 static BOOL
599 is_name_match(const uschar * name, const uschar * pat)
600 {
601 uschar * cp;
602 return *pat == '*'              /* possible wildcard match */
603   ?    *++pat == '.'            /* starts star, dot              */
604     && !Ustrchr(++pat, '*')     /* has no more stars             */
605     && Ustrchr(pat, '.')        /* and has another dot.          */
606     && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
607     && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
608   :    !Ustrchr(pat+1, '*')
609     && strcmpic(name, pat) == 0;
610 }
611
612 /* Compare a list of names with the dnsname elements
613 of the Subject Alternate Name, if any, and the
614 Subject otherwise.
615
616 Arguments:
617         namelist names to compare
618         cert     certificate
619
620 Returns:
621         TRUE/FALSE
622 */
623
624 BOOL
625 tls_is_name_for_cert(const uschar * namelist, void * cert)
626 {
627 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
628 uschar * subjdn;
629 uschar * certname;
630 int cmp_sep = 0;
631 uschar * cmpname;
632
633 if ((altnames = tls_cert_subject_altname(cert, US"dns")))
634   {
635   int alt_sep = '\n';
636   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
637     {
638     const uschar * an = altnames;
639     while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
640       if (is_name_match(cmpname, certname))
641         return TRUE;
642     }
643   }
644
645 else if ((subjdn = tls_cert_subject(cert, NULL)))
646   {
647   int sn_sep = ',';
648
649   dn_to_list(subjdn);
650   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
651     {
652     const uschar * sn = subjdn;
653     while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
654       if (  *certname++ == 'C'
655          && *certname++ == 'N'
656          && *certname++ == '='
657          && is_name_match(cmpname, certname)
658          )
659         return TRUE;
660     }
661   }
662 return FALSE;
663 }
664
665
666 /* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
667 and writes a file by that name.  Our OpenSSL code does the same, using keying
668 info from the library API.
669 The GnuTLS support only works if exim is run by root, not taking advantage of
670 the setuid bit.
671 You can use either the external environment (modulo the keep_environment config)
672 or the add_environment config option for SSLKEYLOGFILE; the latter takes
673 precedence.
674
675 If the path is absolute, require it starts with the spooldir; otherwise delete
676 the env variable.  If relative, prefix the spooldir.
677 */
678 void
679 tls_clean_env(void)
680 {
681 uschar * path = US getenv("SSLKEYLOGFILE");
682 if (path)
683   if (!*path)
684     unsetenv("SSLKEYLOGFILE");
685   else if (*path != '/')
686     {
687     DEBUG(D_tls)
688       debug_printf("prepending spooldir to  env SSLKEYLOGFILE\n");
689     setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
690     }
691   else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
692     {
693     DEBUG(D_tls)
694       debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
695     unsetenv("SSLKEYLOGFILE");
696     }
697 }
698
699 /*************************************************
700 *       Drop privs for checking TLS config      *
701 *************************************************/
702
703 /* We want to validate TLS options during readconf, but do not want to be
704 root when we call into the TLS library, in case of library linkage errors
705 which cause segfaults; before this check, those were always done as the Exim
706 runtime user and it makes sense to continue with that.
707
708 Assumes:  tls_require_ciphers has been set, if it will be
709           exim_user has been set, if it will be
710           exim_group has been set, if it will be
711
712 Returns:  bool for "okay"; false will cause caller to immediately exit.
713 */
714
715 BOOL
716 tls_dropprivs_validate_require_cipher(BOOL nowarn)
717 {
718 const uschar *errmsg;
719 pid_t pid;
720 int rc, status;
721 void (*oldsignal)(int);
722
723 /* If TLS will never be used, no point checking ciphers */
724
725 if (  !tls_advertise_hosts
726    || !*tls_advertise_hosts
727    || Ustrcmp(tls_advertise_hosts, ":") == 0
728    )
729   return TRUE;
730 else if (!nowarn && !tls_certificate)
731   log_write(0, LOG_MAIN,
732     "Warning: No server certificate defined; will use a selfsigned one.\n"
733     " Suggested action: either install a certificate or change tls_advertise_hosts option");
734
735 oldsignal = signal(SIGCHLD, SIG_DFL);
736
737 fflush(NULL);
738 if ((pid = exim_fork(US"cipher-validate")) < 0)
739   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
740
741 if (pid == 0)
742   {
743   /* in some modes, will have dropped privilege already */
744   if (!geteuid())
745     exim_setugid(exim_uid, exim_gid, FALSE,
746         US"calling tls_validate_require_cipher");
747
748   if ((errmsg = tls_validate_require_cipher()))
749     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
750         "tls_require_ciphers invalid: %s", errmsg);
751   fflush(NULL);
752   exim_underbar_exit(EXIT_SUCCESS);
753   }
754
755 do {
756   rc = waitpid(pid, &status, 0);
757 } while (rc < 0 && errno == EINTR);
758
759 DEBUG(D_tls)
760   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
761       (int)pid, status);
762
763 signal(SIGCHLD, oldsignal);
764
765 return status == 0;
766 }
767
768
769
770
771 #endif  /*!DISABLE_TLS*/
772 #endif  /*!MACRO_PREDEF*/
773
774 /* vi: aw ai sw=2
775 */
776 /* End of tls.c */