X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/17c761988f30054827a9951761d93ffeeaad0cb7..817d9f576cdfbc27cf0536be348645baf27d7836:/src/src/readconf.c diff --git a/src/src/readconf.c b/src/src/readconf.c index cdd32be58..750e0d316 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -216,6 +216,7 @@ static optionlist optionlist_config[] = { { "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup }, { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, + { "dns_use_dnssec", opt_int, &dns_use_dnssec }, { "dns_use_edns0", opt_int, &dns_use_edns0 }, /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, @@ -235,7 +236,7 @@ static optionlist optionlist_config[] = { { "gecos_pattern", opt_stringptr, &gecos_pattern }, #ifdef SUPPORT_TLS { "gnutls_compat_mode", opt_bool, &gnutls_compat_mode }, - /* These three gnutls_require_* options stopped working in Exim 4.78 */ + /* These three gnutls_require_* options stopped working in Exim 4.80 */ { "gnutls_require_kx", opt_stringptr, &gnutls_require_kx }, { "gnutls_require_mac", opt_stringptr, &gnutls_require_mac }, { "gnutls_require_protocols", opt_stringptr, &gnutls_require_proto }, @@ -415,8 +416,12 @@ static optionlist optionlist_config[] = { { "tls_advertise_hosts", opt_stringptr, &tls_advertise_hosts }, { "tls_certificate", opt_stringptr, &tls_certificate }, { "tls_crl", opt_stringptr, &tls_crl }, + { "tls_dh_max_bits", opt_int, &tls_dh_max_bits }, { "tls_dhparam", opt_stringptr, &tls_dhparam }, - { "tls_on_connect_ports", opt_stringptr, &tls_on_connect_ports }, +# if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS) + { "tls_ocsp_file", opt_stringptr, &tls_ocsp_file }, +# endif + { "tls_on_connect_ports", opt_stringptr, &tls_in.on_connect_ports }, { "tls_privatekey", opt_stringptr, &tls_privatekey }, { "tls_remember_esmtp", opt_bool, &tls_remember_esmtp }, { "tls_require_ciphers", opt_stringptr, &tls_require_ciphers }, @@ -2146,13 +2151,14 @@ Arguments: resides. oltop points to the option list in which ol exists last one more than the offset of the last entry in optop + no_labels do not show "foo = " at the start. Returns: nothing */ static void print_ol(optionlist *ol, uschar *name, void *options_block, - optionlist *oltop, int last) + optionlist *oltop, int last, BOOL no_labels) { struct passwd *pw; struct group *gr; @@ -2174,7 +2180,11 @@ if (ol == NULL) if (!admin_user && (ol->type & opt_secure) != 0) { - printf("%s = \n", name); + const char * const hidden = ""; + if (no_labels) + printf("%s\n", hidden); + else + printf("%s = %s\n", name, hidden); return; } @@ -2193,11 +2203,13 @@ switch(ol->type & opt_mask) case opt_stringptr: case opt_rewrite: /* Show the text value */ s = *((uschar **)value); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing2(s, FALSE)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing2(s, FALSE)); break; case opt_int: - printf("%s = %d\n", name, *((int *)value)); + if (!no_labels) printf("%s = ", name); + printf("%d\n", *((int *)value)); break; case opt_mkint: @@ -2212,23 +2224,30 @@ switch(ol->type & opt_mask) c = 'M'; x >>= 10; } - printf("%s = %d%c\n", name, x, c); + if (!no_labels) printf("%s = ", name); + printf("%d%c\n", x, c); + } + else + { + if (!no_labels) printf("%s = ", name); + printf("%d\n", x); } - else printf("%s = %d\n", name, x); } break; case opt_Kint: { int x = *((int *)value); - if (x == 0) printf("%s = 0\n", name); - else if ((x & 1023) == 0) printf("%s = %dM\n", name, x >> 10); - else printf("%s = %dK\n", name, x); + if (!no_labels) printf("%s = ", name); + if (x == 0) printf("0\n"); + else if ((x & 1023) == 0) printf("%dM\n", x >> 10); + else printf("%dK\n", x); } break; case opt_octint: - printf("%s = %#o\n", name, *((int *)value)); + if (!no_labels) printf("%s = ", name); + printf("%#o\n", *((int *)value)); break; /* Can be negative only when "unset", in which case integer */ @@ -2240,7 +2259,8 @@ switch(ol->type & opt_mask) int d = 100; if (x < 0) printf("%s =\n", name); else { - printf("%s = %d.", name, x/1000); + if (!no_labels) printf("%s = ", name); + printf("%d.", x/1000); do { printf("%d", f/d); @@ -2266,7 +2286,8 @@ switch(ol->type & opt_mask) if (options_block != NULL) value2 = (void *)((uschar *)options_block + (long int)value2); s = *((uschar **)value2); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing(s)); break; } } @@ -2274,14 +2295,15 @@ switch(ol->type & opt_mask) /* Else fall through */ case opt_uid: + if (!no_labels) printf("%s = ", name); if (! *get_set_flag(name, oltop, last, options_block)) - printf("%s =\n", name); + printf("\n"); else { pw = getpwuid(*((uid_t *)value)); if (pw == NULL) - printf("%s = %ld\n", name, (long int)(*((uid_t *)value))); - else printf("%s = %s\n", name, pw->pw_name); + printf("%ld\n", (long int)(*((uid_t *)value))); + else printf("%s\n", pw->pw_name); } break; @@ -2298,7 +2320,8 @@ switch(ol->type & opt_mask) if (options_block != NULL) value2 = (void *)((uschar *)options_block + (long int)value2); s = *((uschar **)value2); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing(s)); break; } } @@ -2306,31 +2329,34 @@ switch(ol->type & opt_mask) /* Else fall through */ case opt_gid: + if (!no_labels) printf("%s = ", name); if (! *get_set_flag(name, oltop, last, options_block)) - printf("%s =\n", name); + printf("\n"); else { gr = getgrgid(*((int *)value)); if (gr == NULL) - printf("%s = %ld\n", name, (long int)(*((int *)value))); - else printf("%s = %s\n", name, gr->gr_name); + printf("%ld\n", (long int)(*((int *)value))); + else printf("%s\n", gr->gr_name); } break; case opt_uidlist: uidlist = *((uid_t **)value); - printf("%s =", name); + if (!no_labels) printf("%s =", name); if (uidlist != NULL) { int i; uschar sep = ' '; + if (no_labels) sep = '\0'; for (i = 1; i <= (int)(uidlist[0]); i++) { uschar *name = NULL; pw = getpwuid(uidlist[i]); if (pw != NULL) name = US pw->pw_name; - if (name != NULL) printf("%c%s", sep, name); - else printf("%c%ld", sep, (long int)(uidlist[i])); + if (sep != '\0') printf("%c", sep); + if (name != NULL) printf("%s", name); + else printf("%ld", (long int)(uidlist[i])); sep = ':'; } } @@ -2339,18 +2365,20 @@ switch(ol->type & opt_mask) case opt_gidlist: gidlist = *((gid_t **)value); - printf("%s =", name); + if (!no_labels) printf("%s =", name); if (gidlist != NULL) { int i; uschar sep = ' '; + if (no_labels) sep = '\0'; for (i = 1; i <= (int)(gidlist[0]); i++) { uschar *name = NULL; gr = getgrgid(gidlist[i]); if (gr != NULL) name = US gr->gr_name; - if (name != NULL) printf("%c%s", sep, name); - else printf("%c%ld", sep, (long int)(gidlist[i])); + if (sep != '\0') printf("%c", sep); + if (name != NULL) printf("%s", name); + else printf("%ld", (long int)(gidlist[i])); sep = ':'; } } @@ -2358,14 +2386,15 @@ switch(ol->type & opt_mask) break; case opt_time: - printf("%s = %s\n", name, readconf_printtime(*((int *)value))); + if (!no_labels) printf("%s = ", name); + printf("%s\n", readconf_printtime(*((int *)value))); break; case opt_timelist: { int i; int *list = (int *)value; - printf("%s = ", name); + if (!no_labels) printf("%s = ", name); for (i = 0; i < list[1]; i++) printf("%s%s", (i == 0)? "" : ":", readconf_printtime(list[i+2])); printf("\n"); @@ -2388,7 +2417,8 @@ switch(ol->type & opt_mask) s = *((uschar **)value2); if (s != NULL) { - printf("%s = %s\n", name, string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", string_printing(s)); break; } /* s == NULL => string not set; fall through */ @@ -2434,12 +2464,13 @@ driver whose options are to be printed. Arguments: name option name if type == NULL; else driver name type NULL or driver type name, as described above + no_labels avoid the "foo = " at the start of an item Returns: nothing */ void -readconf_print(uschar *name, uschar *type) +readconf_print(uschar *name, uschar *type, BOOL no_labels) { BOOL names_only = FALSE; optionlist *ol; @@ -2466,8 +2497,11 @@ if (type == NULL) if (t != NULL) { found = TRUE; - printf("%slist %s = %s\n", types[i], name+1, - ((namedlist_block *)(t->data.ptr))->string); + if (no_labels) + printf("%s\n", ((namedlist_block *)(t->data.ptr))->string); + else + printf("%slist %s = %s\n", types[i], name+1, + ((namedlist_block *)(t->data.ptr))->string); } } @@ -2490,7 +2524,9 @@ if (type == NULL) ol < optionlist_config + optionlist_config_size; ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, NULL, optionlist_config, optionlist_config_size); + print_ol(ol, US ol->name, NULL, + optionlist_config, optionlist_config_size, + no_labels); } return; } @@ -2504,7 +2540,7 @@ if (type == NULL) ol < local_scan_options + local_scan_options_count; ol++) { print_ol(ol, US ol->name, NULL, local_scan_options, - local_scan_options_count); + local_scan_options_count, no_labels); } #endif return; @@ -2564,7 +2600,7 @@ if (type == NULL) else { print_ol(find_option(name, optionlist_config, optionlist_config_size), - name, NULL, optionlist_config, optionlist_config_size); + name, NULL, optionlist_config, optionlist_config_size, no_labels); return; } } @@ -2637,14 +2673,14 @@ for (; d != NULL; d = d->next) for (ol = ol2; ol < ol2 + size; ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, d, ol2, size); + print_ol(ol, US ol->name, d, ol2, size, no_labels); } for (ol = d->info->options; ol < d->info->options + *(d->info->options_count); ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, d, d->info->options, *(d->info->options_count)); + print_ol(ol, US ol->name, d, d->info->options, *(d->info->options_count), no_labels); } if (name != NULL) return; } @@ -2768,6 +2804,71 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malformed ratelimit data: %s", s); +/************************************************* +* Drop privs for checking TLS config * +*************************************************/ + +/* We want to validate TLS options during readconf, but do not want to be +root when we call into the TLS library, in case of library linkage errors +which cause segfaults; before this check, those were always done as the Exim +runtime user and it makes sense to continue with that. + +Assumes: tls_require_ciphers has been set, if it will be + exim_user has been set, if it will be + exim_group has been set, if it will be + +Returns: bool for "okay"; false will cause caller to immediately exit. +*/ + +#ifdef SUPPORT_TLS +static BOOL +tls_dropprivs_validate_require_cipher(void) +{ +const uschar *errmsg; +pid_t pid; +int rc, status; +void (*oldsignal)(int); + +oldsignal = signal(SIGCHLD, SIG_DFL); + +fflush(NULL); +if ((pid = fork()) < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check"); + +if (pid == 0) + { + /* in some modes, will have dropped privilege already */ + if (!geteuid()) + exim_setugid(exim_uid, exim_gid, FALSE, + US"calling tls_validate_require_cipher"); + + errmsg = tls_validate_require_cipher(); + if (errmsg) + { + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_require_ciphers invalid: %s", errmsg); + } + fflush(NULL); + _exit(0); + } + +do { + rc = waitpid(pid, &status, 0); +} while (rc < 0 && errno == EINTR); + +DEBUG(D_tls) + debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n", + (int)pid, status); + +signal(SIGCHLD, oldsignal); + +return status == 0; +} +#endif /* SUPPORT_TLS */ + + + + /************************************************* * Read main configuration options * *************************************************/ @@ -3218,6 +3319,18 @@ if ((tls_verify_hosts != NULL || tls_try_verify_hosts != NULL) && "tls_%sverify_hosts is set, but tls_verify_certificates is not set", (tls_verify_hosts != NULL)? "" : "try_"); +/* This also checks that the library linkage is working and we can call +routines in it, so call even if tls_require_ciphers is unset */ +if (!tls_dropprivs_validate_require_cipher()) + exit(1); + +/* Magic number: at time of writing, 1024 has been the long-standing value +used by so many clients, and what Exim used to use always, that it makes +sense to just min-clamp this max-clamp at that. */ +if (tls_dh_max_bits < 1024) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_dh_max_bits is too small, must be at least 1024 for interop"); + /* If openssl_options is set, validate it */ if (openssl_options != NULL) {