From: Jeremy Harris Date: Fri, 2 May 2014 17:50:34 +0000 (+0100) Subject: Certificate variables and field-extractor expansions. Bug 1358 X-Git-Tag: exim-4_83_RC1~40 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/9d1c15ef45fcc8809349378922de20ae9a774c75?ds=inline Certificate variables and field-extractor expansions. Bug 1358 --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index afc15d433..ec9367582 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -8875,6 +8875,41 @@ the expansion result is an empty string. If the ACL returns defer the result is a forced-fail. Otherwise the expansion fails. +.new +.vitem "&*${certextract{*&<&'field'&>&*}{*&<&'certificate'&>&*}&&& + {*&<&'string2'&>&*}{*&<&'string3'&>&*}}*&" +.cindex "expansion" "extracting cerificate fields" +.cindex "&%certextract%&" "certificate fields" +.cindex "certificate" "extracting fields" +The <&'certificate'&> must be a variable of type certificate. +The field name is expanded and used to retrive the relevant field from +the certificate. Supported fields are: +.display +version +serial_number +subject +issuer +notbefore +notafter +signature_algorithm +signature +subject_altname +ocsp_uri +crl_uri +.endd +If the field is found, +<&'string2'&> is expanded, and replaces the whole item; +otherwise <&'string3'&> is used. During the expansion of <&'string2'&> the +variable &$value$& contains the value that has been extracted. Afterwards, it +is restored to any previous value it might have had. + +If {<&'string3'&>} is omitted, the item is replaced by an empty string if the +key is not found. If {<&'string2'&>} is also omitted, the value that was +extracted is used. + +Field values are presented in human-readable form. +.wen + .vitem "&*${dlfunc{*&<&'file'&>&*}{*&<&'function'&>&*}{*&<&'arg'&>&*}&&& {*&<&'arg'&>&*}...}*&" .cindex &%dlfunc%& @@ -12253,6 +12288,40 @@ on an outbound SMTP connection; the meaning of this depends upon the TLS implementation used. If TLS has not been negotiated, the value will be 0. +.new +.vitem &$tls_in_ourcert$& +.vindex "&$tls_in_ourcert$&" +This variable refers to the certificate presented to the peer of an +inbound connection when the message was received. +It is only useful as the argument of a +&%certextract%& expansion item or the name for a &%def%& expansion condition. +.wen + +.new +.vitem &$tls_in_peercert$& +.vindex "&$tls_in_peercert$&" +This variable refers to the certificate presented by the peer of an +inbound connection when the message was received. +It is only useful as the argument of a +&%certextract%& expansion item or the name for a &%def%& expansion condition. +.wen + +.new +.vitem &$tls_out_ourcert$& +.vindex "&$tls_out_ourcert$&" +This variable refers to the certificate presented to the peer of an +outbound connection. It is only useful as the argument of a +&%certextract%& expansion item or the name for a &%def%& expansion condition. +.wen + +.new +.vitem &$tls_out_peercert$& +.vindex "&$tls_out_peercert$&" +This variable refers to the certificate presented by the peer of an +outbound connection. It is only useful as the argument of a +&%certextract%& expansion item or the name for a &%def%& expansion condition. +.wen + .vitem &$tls_in_certificate_verified$& .vindex "&$tls_in_certificate_verified$&" This variable is set to &"1"& if a TLS certificate was verified when the diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 172748584..c98528884 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -104,6 +104,9 @@ TL/10 Bugzilla 1454: New -oMm option to pass message reference to Exim. Requires trusted mode and valid format message id, aborts otherwise. Patch contributed by Heiko Schlichting. +JH/20 New expansion variables tls_(in,out)_(our,peer)cert, and expansion item + certextract with support for various fields. Bug 1358. + Exim version 4.82 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 33c66ceb9..b6fc576bd 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -44,6 +44,9 @@ Version 4.83 9. Support for DNSSEC on outbound connections. +10. New variables "tls_(in,out)_(our,peer)cert" and expansion item + "certextract" to extract fields from them. + Version 4.82 ------------ diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 8209969f6..0caf8604b 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -577,7 +577,7 @@ spool_out.o: $(HDRS) spool_out.c std-crypto.o: $(HDRS) std-crypto.c store.o: $(HDRS) store.c string.o: $(HDRS) string.c -tls.o: $(HDRS) tls.c tls-gnu.c tls-openssl.c +tls.o: $(HDRS) tls.c tls-gnu.c tlscert-gnu.c tls-openssl.c tlscert-openssl.c tod.o: $(HDRS) tod.c transport.o: $(HDRS) transport.c tree.o: $(HDRS) tree.c diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 2eb8a967e..01cd21f1c 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -233,6 +233,8 @@ ln -s ../src/std-crypto.c std-crypto.c ln -s ../src/store.c store.c ln -s ../src/string.c string.c ln -s ../src/tls.c tls.c +ln -s ../src/tlscert-gnu.c tlscert-gnu.c +ln -s ../src/tlscert-openssl.c tlscert-openssl.c ln -s ../src/tls-gnu.c tls-gnu.c ln -s ../src/tls-openssl.c tls-openssl.c ln -s ../src/tod.c tod.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 1e7a8a18a..fff0e2fd0 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1057,7 +1057,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) (void)close(addr->return_file); } -/* The sucess case happens only after delivery by a transport. */ +/* The success case happens only after delivery by a transport. */ if (result == OK) { @@ -1073,10 +1073,8 @@ if (result == OK) DEBUG(D_deliver) debug_printf("%s delivered\n", addr->address); if (addr->parent == NULL) - { deliver_msglog("%s %s: %s%s succeeded\n", now, addr->address, driver_name, driver_kind); - } else { deliver_msglog("%s %s <%s>: %s%s succeeded\n", now, addr->address, @@ -1084,7 +1082,28 @@ if (result == OK) child_done(addr, now); } + /* Certificates for logging (via TPDA) */ + #ifdef SUPPORT_TLS + tls_out.ourcert = addr->ourcert; + addr->ourcert = NULL; + tls_out.peercert = addr->peercert; + addr->peercert = NULL; + #endif + delivery_log(LOG_MAIN, addr, logchar, NULL); + + #ifdef SUPPORT_TLS + if (tls_out.ourcert) + { + tls_free_cert(tls_out.ourcert); + tls_out.ourcert = NULL; + } + if (tls_out.peercert) + { + tls_free_cert(tls_out.peercert); + tls_out.peercert = NULL; + } + #endif } @@ -2957,27 +2976,51 @@ while (!done) #ifdef SUPPORT_TLS case 'X': - if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ - addr->cipher = (*ptr)? string_copy(ptr) : NULL; - while (*ptr++); - addr->peerdn = (*ptr)? string_copy(ptr) : NULL; + if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ + switch (*ptr++) + { + case '1': + addr->cipher = NULL; + addr->peerdn = NULL; + + if (*ptr) + addr->cipher = string_copy(ptr); + while (*ptr++); + if (*ptr) + { + addr->peerdn = string_copy(ptr); + } + break; + + case '2': + addr->peercert = NULL; + if (*ptr) + (void) tls_import_cert(ptr, &addr->peercert); + break; + + case '3': + addr->ourcert = NULL; + if (*ptr) + (void) tls_import_cert(ptr, &addr->ourcert); + break; + } while (*ptr++); break; #endif case 'C': /* client authenticator information */ switch (*ptr++) - { - case '1': - addr->authenticator = (*ptr)? string_copy(ptr) : NULL; - break; - case '2': - addr->auth_id = (*ptr)? string_copy(ptr) : NULL; - break; - case '3': - addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL; - break; - } + { + case '1': + addr->authenticator = (*ptr)? string_copy(ptr) : NULL; + break; + case '2': + addr->auth_id = (*ptr)? string_copy(ptr) : NULL; + break; + case '3': + addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL; + break; + } while (*ptr++); break; @@ -4054,18 +4097,41 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Use an X item only if there's something to send */ #ifdef SUPPORT_TLS - if (addr->cipher != NULL) + if (addr->cipher) { ptr = big_buffer; - sprintf(CS ptr, "X%.128s", addr->cipher); + sprintf(CS ptr, "X1%.128s", addr->cipher); while(*ptr++); - if (addr->peerdn == NULL) *ptr++ = 0; else + if (!addr->peerdn) + *ptr++ = 0; + else { sprintf(CS ptr, "%.512s", addr->peerdn); while(*ptr++); } + rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); } + if (addr->peercert) + { + ptr = big_buffer; + *ptr++ = 'X'; *ptr++ = '2'; + if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert)) + while(*ptr++); + else + *ptr++ = 0; + rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + } + if (addr->ourcert) + { + ptr = big_buffer; + *ptr++ = 'X'; *ptr++ = '3'; + if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert)) + while(*ptr++); + else + *ptr++ = 0; + rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + } #endif if (client_authenticator) diff --git a/src/src/expand.c b/src/src/expand.c index 54b3abc54..34fb0346e 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -93,6 +93,9 @@ bcrypt ({CRYPT}$2a$). +#ifndef nelements +# define nelements(arr) (sizeof(arr) / sizeof(*arr)) +#endif /************************************************* * Local statics and tables * @@ -103,6 +106,7 @@ alphabetical order. */ static uschar *item_table[] = { US"acl", + US"certextract", US"dlfunc", US"extract", US"filter", @@ -127,6 +131,7 @@ static uschar *item_table[] = { enum { EITEM_ACL, + EITEM_CERTEXTRACT, EITEM_DLFUNC, EITEM_EXTRACT, EITEM_FILTER, @@ -387,7 +392,8 @@ enum { vtype_host_lookup, /* value not used; get host name */ vtype_load_avg, /* value not used; result is int from os_getloadavg */ vtype_pspace, /* partition space; value is T/F for spool/log */ - vtype_pinodes /* partition inodes; value is T/F for spool/log */ + vtype_pinodes, /* partition inodes; value is T/F for spool/log */ + vtype_cert /* SSL certificate */ #ifndef DISABLE_DKIM ,vtype_dkim /* Lookup of value in DKIM signature */ #endif @@ -665,6 +671,8 @@ static var_entry var_table[] = { { "tls_in_bits", vtype_int, &tls_in.bits }, { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified }, { "tls_in_cipher", vtype_stringptr, &tls_in.cipher }, + { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, + { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) { "tls_in_sni", vtype_stringptr, &tls_in.sni }, @@ -672,6 +680,8 @@ static var_entry var_table[] = { { "tls_out_bits", vtype_int, &tls_out.bits }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, + { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, + { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) { "tls_out_sni", vtype_stringptr, &tls_out.sni }, @@ -1065,6 +1075,23 @@ return NULL; +static var_entry * +find_var_ent(uschar * name) +{ +int first = 0; +int last = var_table_size; + +while (last > first) + { + int middle = (first + last)/2; + int c = Ustrcmp(name, var_table[middle].name); + + if (c > 0) { first = middle + 1; continue; } + if (c < 0) { last = middle; continue; } + return &var_table[middle]; + } +return NULL; +} /************************************************* * Extract numbered subfield from string * @@ -1140,7 +1167,7 @@ return fieldtext; static uschar * -expand_getlistele (int field, uschar *list) +expand_getlistele(int field, uschar * list) { uschar * tlist= list; int sep= 0; @@ -1156,6 +1183,68 @@ while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); } + +/* Certificate fields, by name. Worry about by-OID later */ + +#ifdef SUPPORT_TLS +typedef struct +{ +uschar * name; +uschar * (*getfn)(void * cert); +} certfield; +static certfield certfields[] = +{ /* linear search; no special order */ + { US"version", &tls_cert_version }, + { US"serial_number", &tls_cert_serial_number }, + { US"subject", &tls_cert_subject }, + { US"notbefore", &tls_cert_not_before }, + { US"notafter", &tls_cert_not_after }, + { US"issuer", &tls_cert_issuer }, + { US"signature", &tls_cert_signature }, + { US"signature_algorithm", &tls_cert_signature_algorithm }, + { US"subject_altname", &tls_cert_subject_altname }, + { US"ocsp_uri", &tls_cert_ocsp_uri }, + { US"crl_uri", &tls_cert_crl_uri }, +}; + +static uschar * +expand_getcertele(uschar * field, uschar * certvar) +{ +var_entry * vp; +certfield * cp; + +if (!(vp = find_var_ent(certvar))) + { + expand_string_message = + string_sprintf("no variable named \"%s\"", certvar); + return NULL; /* Unknown variable name */ + } +/* NB this stops us passing certs around in variable. Might +want to do that in future */ +if (vp->type != vtype_cert) + { + expand_string_message = + string_sprintf("\"%s\" is not a certificate", certvar); + return NULL; /* Unknown variable name */ + } +if (!*(void **)vp->value) + return NULL; + +if (*field >= '0' && *field <= '9') + return tls_cert_ext_by_oid(*(void **)vp->value, field, 0); + +for(cp = certfields; + cp < certfields + nelements(certfields); + cp++) + if (Ustrcmp(cp->name, field) == 0) + return (*cp->getfn)( *(void **)vp->value ); + +expand_string_message = + string_sprintf("bad field selector \"%s\" for certextract", field); +return NULL; +} +#endif /*SUPPORT_TLS*/ + /************************************************* * Extract a substring from a string * *************************************************/ @@ -1551,8 +1640,10 @@ Returns: NULL if the variable does not exist, or static uschar * find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize) { -int first = 0; -int last = var_table_size; +var_entry * vp; +uschar *s, *domain; +uschar **ss; +void * val; /* Handle ACL variables, whose names are of the form acl_cxxx or acl_mxxx. Originally, xxx had to be a number in the range 0-9 (later 0-19), but from @@ -1585,203 +1676,198 @@ if (Ustrncmp(name, "auth", 4) == 0) /* For all other variables, search the table */ -while (last > first) - { - uschar *s, *domain; - uschar **ss; - int middle = (first + last)/2; - int c = Ustrcmp(name, var_table[middle].name); - - if (c > 0) { first = middle + 1; continue; } - if (c < 0) { last = middle; continue; } +if (!(vp = find_var_ent(name))) + return NULL; /* Unknown variable name */ - /* Found an existing variable. If in skipping state, the value isn't needed, - and we want to avoid processing (such as looking up the host name). */ +/* Found an existing variable. If in skipping state, the value isn't needed, +and we want to avoid processing (such as looking up the host name). */ - if (skipping) return US""; +if (skipping) + return US""; - switch (var_table[middle].type) +val = vp->value; +switch (vp->type) + { + case vtype_filter_int: + if (!filter_running) return NULL; + /* Fall through */ + /* VVVVVVVVVVVV */ + case vtype_int: + sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */ + return var_buffer; + + case vtype_ino: + sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */ + return var_buffer; + + case vtype_gid: + sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */ + return var_buffer; + + case vtype_uid: + sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */ + return var_buffer; + + case vtype_bool: + sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */ + return var_buffer; + + case vtype_stringptr: /* Pointer to string */ + s = *((uschar **)(val)); + return (s == NULL)? US"" : s; + + case vtype_pid: + sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */ + return var_buffer; + + case vtype_load_avg: + sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */ + return var_buffer; + + case vtype_host_lookup: /* Lookup if not done so */ + if (sender_host_name == NULL && sender_host_address != NULL && + !host_lookup_failed && host_name_lookup() == OK) + host_build_sender_fullhost(); + return (sender_host_name == NULL)? US"" : sender_host_name; + + case vtype_localpart: /* Get local part from address */ + s = *((uschar **)(val)); + if (s == NULL) return US""; + domain = Ustrrchr(s, '@'); + if (domain == NULL) return s; + if (domain - s > sizeof(var_buffer) - 1) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT + " in string expansion", sizeof(var_buffer)); + Ustrncpy(var_buffer, s, domain - s); + var_buffer[domain - s] = 0; + return var_buffer; + + case vtype_domain: /* Get domain from address */ + s = *((uschar **)(val)); + if (s == NULL) return US""; + domain = Ustrrchr(s, '@'); + return (domain == NULL)? US"" : domain + 1; + + case vtype_msgheaders: + return find_header(NULL, exists_only, newsize, FALSE, NULL); + + case vtype_msgheaders_raw: + return find_header(NULL, exists_only, newsize, TRUE, NULL); + + case vtype_msgbody: /* Pointer to msgbody string */ + case vtype_msgbody_end: /* Ditto, the end of the msg */ + ss = (uschar **)(val); + if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ { - case vtype_filter_int: - if (!filter_running) return NULL; - /* Fall through */ - /* VVVVVVVVVVVV */ - case vtype_int: - sprintf(CS var_buffer, "%d", *(int *)(var_table[middle].value)); /* Integer */ - return var_buffer; - - case vtype_ino: - sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(var_table[middle].value))); /* Inode */ - return var_buffer; - - case vtype_gid: - sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(var_table[middle].value))); /* gid */ - return var_buffer; - - case vtype_uid: - sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(var_table[middle].value))); /* uid */ - return var_buffer; - - case vtype_bool: - sprintf(CS var_buffer, "%s", *(BOOL *)(var_table[middle].value) ? "yes" : "no"); /* bool */ - return var_buffer; - - case vtype_stringptr: /* Pointer to string */ - s = *((uschar **)(var_table[middle].value)); - return (s == NULL)? US"" : s; - - case vtype_pid: - sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */ - return var_buffer; - - case vtype_load_avg: - sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */ - return var_buffer; - - case vtype_host_lookup: /* Lookup if not done so */ - if (sender_host_name == NULL && sender_host_address != NULL && - !host_lookup_failed && host_name_lookup() == OK) - host_build_sender_fullhost(); - return (sender_host_name == NULL)? US"" : sender_host_name; - - case vtype_localpart: /* Get local part from address */ - s = *((uschar **)(var_table[middle].value)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - if (domain == NULL) return s; - if (domain - s > sizeof(var_buffer) - 1) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT - " in string expansion", sizeof(var_buffer)); - Ustrncpy(var_buffer, s, domain - s); - var_buffer[domain - s] = 0; - return var_buffer; - - case vtype_domain: /* Get domain from address */ - s = *((uschar **)(var_table[middle].value)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - return (domain == NULL)? US"" : domain + 1; - - case vtype_msgheaders: - return find_header(NULL, exists_only, newsize, FALSE, NULL); - - case vtype_msgheaders_raw: - return find_header(NULL, exists_only, newsize, TRUE, NULL); - - case vtype_msgbody: /* Pointer to msgbody string */ - case vtype_msgbody_end: /* Ditto, the end of the msg */ - ss = (uschar **)(var_table[middle].value); - if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ + uschar *body; + off_t start_offset = SPOOL_DATA_START_OFFSET; + int len = message_body_visible; + if (len > message_size) len = message_size; + *ss = body = store_malloc(len+1); + body[0] = 0; + if (vp->type == vtype_msgbody_end) { - uschar *body; - off_t start_offset = SPOOL_DATA_START_OFFSET; - int len = message_body_visible; - if (len > message_size) len = message_size; - *ss = body = store_malloc(len+1); - body[0] = 0; - if (var_table[middle].type == vtype_msgbody_end) - { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0) - { - start_offset = statbuf.st_size - len; - if (start_offset < SPOOL_DATA_START_OFFSET) - start_offset = SPOOL_DATA_START_OFFSET; - } - } - lseek(deliver_datafile, start_offset, SEEK_SET); - len = read(deliver_datafile, body, len); - if (len > 0) - { - body[len] = 0; - if (message_body_newlines) /* Separate loops for efficiency */ - { - while (len > 0) - { if (body[--len] == 0) body[len] = ' '; } - } - else - { - while (len > 0) - { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; } - } - } + struct stat statbuf; + if (fstat(deliver_datafile, &statbuf) == 0) + { + start_offset = statbuf.st_size - len; + if (start_offset < SPOOL_DATA_START_OFFSET) + start_offset = SPOOL_DATA_START_OFFSET; + } + } + lseek(deliver_datafile, start_offset, SEEK_SET); + len = read(deliver_datafile, body, len); + if (len > 0) + { + body[len] = 0; + if (message_body_newlines) /* Separate loops for efficiency */ + { + while (len > 0) + { if (body[--len] == 0) body[len] = ' '; } + } + else + { + while (len > 0) + { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; } + } } - return (*ss == NULL)? US"" : *ss; + } + return (*ss == NULL)? US"" : *ss; - case vtype_todbsdin: /* BSD inbox time of day */ - return tod_stamp(tod_bsdin); + case vtype_todbsdin: /* BSD inbox time of day */ + return tod_stamp(tod_bsdin); - case vtype_tode: /* Unix epoch time of day */ - return tod_stamp(tod_epoch); + case vtype_tode: /* Unix epoch time of day */ + return tod_stamp(tod_epoch); - case vtype_todel: /* Unix epoch/usec time of day */ - return tod_stamp(tod_epoch_l); + case vtype_todel: /* Unix epoch/usec time of day */ + return tod_stamp(tod_epoch_l); - case vtype_todf: /* Full time of day */ - return tod_stamp(tod_full); + case vtype_todf: /* Full time of day */ + return tod_stamp(tod_full); - case vtype_todl: /* Log format time of day */ - return tod_stamp(tod_log_bare); /* (without timezone) */ + case vtype_todl: /* Log format time of day */ + return tod_stamp(tod_log_bare); /* (without timezone) */ - case vtype_todzone: /* Time zone offset only */ - return tod_stamp(tod_zone); + case vtype_todzone: /* Time zone offset only */ + return tod_stamp(tod_zone); - case vtype_todzulu: /* Zulu time */ - return tod_stamp(tod_zulu); + case vtype_todzulu: /* Zulu time */ + return tod_stamp(tod_zulu); - case vtype_todlf: /* Log file datestamp tod */ - return tod_stamp(tod_log_datestamp_daily); + case vtype_todlf: /* Log file datestamp tod */ + return tod_stamp(tod_log_datestamp_daily); - case vtype_reply: /* Get reply address */ - s = find_header(US"reply-to:", exists_only, newsize, TRUE, - headers_charset); - if (s != NULL) while (isspace(*s)) s++; - if (s == NULL || *s == 0) - { - *newsize = 0; /* For the *s==0 case */ - s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset); - } - if (s != NULL) - { - uschar *t; - while (isspace(*s)) s++; - for (t = s; *t != 0; t++) if (*t == '\n') *t = ' '; - while (t > s && isspace(t[-1])) t--; - *t = 0; - } - return (s == NULL)? US"" : s; + case vtype_reply: /* Get reply address */ + s = find_header(US"reply-to:", exists_only, newsize, TRUE, + headers_charset); + if (s != NULL) while (isspace(*s)) s++; + if (s == NULL || *s == 0) + { + *newsize = 0; /* For the *s==0 case */ + s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset); + } + if (s != NULL) + { + uschar *t; + while (isspace(*s)) s++; + for (t = s; *t != 0; t++) if (*t == '\n') *t = ' '; + while (t > s && isspace(t[-1])) t--; + *t = 0; + } + return (s == NULL)? US"" : s; - case vtype_string_func: - { - uschar * (*fn)() = var_table[middle].value; - return fn(); - } + case vtype_string_func: + { + uschar * (*fn)() = val; + return fn(); + } - case vtype_pspace: - { - int inodes; - sprintf(CS var_buffer, "%d", - receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes)); - } - return var_buffer; + case vtype_pspace: + { + int inodes; + sprintf(CS var_buffer, "%d", + receive_statvfs(val == (void *)TRUE, &inodes)); + } + return var_buffer; - case vtype_pinodes: - { - int inodes; - (void) receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes); - sprintf(CS var_buffer, "%d", inodes); - } - return var_buffer; + case vtype_pinodes: + { + int inodes; + (void) receive_statvfs(val == (void *)TRUE, &inodes); + sprintf(CS var_buffer, "%d", inodes); + } + return var_buffer; - #ifndef DISABLE_DKIM - case vtype_dkim: - return dkim_exim_expand_query((int)(long)var_table[middle].value); - #endif + case vtype_cert: + return *(void **)val ? US"" : US""; - } - } + #ifndef DISABLE_DKIM + case vtype_dkim: + return dkim_exim_expand_query((int)(long)val); + #endif -return NULL; /* Unknown variable name */ + } } @@ -1790,21 +1876,8 @@ return NULL; /* Unknown variable name */ void modify_variable(uschar *name, void * value) { -int first = 0; -int last = var_table_size; - -while (last > first) - { - int middle = (first + last)/2; - int c = Ustrcmp(name, var_table[middle].name); - - if (c > 0) { first = middle + 1; continue; } - if (c < 0) { last = middle; continue; } - - /* Found an existing variable; change the item it refers to */ - var_table[middle].value = value; - return; - } +var_entry * vp; +if ((vp = find_var_ent(name))) vp->value = value; return; /* Unknown variable name, fail silently */ } @@ -5279,6 +5352,79 @@ while (*s != 0) continue; } +#ifdef SUPPORT_TLS + case EITEM_CERTEXTRACT: + { + int i; + int field_number = 1; + uschar *save_lookup_value = lookup_value; + uschar *sub[2]; + int save_expand_nmax = + save_expand_strings(save_expand_nstring, save_expand_nlength); + + /* Read the field argument */ + while (isspace(*s)) s++; + if (*s != '{') /*}*/ + goto EXPAND_FAILED_CURLY; + sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + if (!sub[0]) goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + /* strip spaces fore & aft */ + { + int len; + int x = 0; + uschar *p = sub[0]; + + while (isspace(*p)) p++; + sub[0] = p; + + len = Ustrlen(p); + while (len > 0 && isspace(p[len-1])) len--; + p[len] = 0; + } + + /* inspect the cert argument */ + while (isspace(*s)) s++; + if (*s != '{') /*}*/ + goto EXPAND_FAILED_CURLY; + if (*++s != '$') + { + expand_string_message = US"second argument of \"certextract\" must " + "be a certificate variable"; + goto EXPAND_FAILED; + } + sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok); + if (!sub[1]) goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + if (skipping) + lookup_value = NULL; + else + { + lookup_value = expand_getcertele(sub[0], sub[1]); + if (*expand_string_message) goto EXPAND_FAILED; + } + switch(process_yesno( + skipping, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + &size, /* output size */ + &ptr, /* output current point */ + US"extract", /* condition type */ + &resetok)) + { + case 1: goto EXPAND_FAILED; /* when all is well, the */ + case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ + } + + restore_expand_strings(save_expand_nmax, save_expand_nstring, + save_expand_nlength); + continue; + } +#endif /*SUPPORT_TLS*/ + /* Handle list operations */ case EITEM_FILTER: diff --git a/src/src/functions.h b/src/src/functions.h index 599afd206..8751a006e 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -25,6 +25,20 @@ extern const char * std_dh_prime_default(void); extern const char * std_dh_prime_named(const uschar *); + +extern uschar * tls_cert_crl_uri(void *); +extern uschar * tls_cert_ext_by_oid(void *, uschar *, int); +extern uschar * tls_cert_issuer(void *); +extern uschar * tls_cert_not_before(void *); +extern uschar * tls_cert_not_after(void *); +extern uschar * tls_cert_ocsp_uri(void *); +extern uschar * tls_cert_serial_number(void *); +extern uschar * tls_cert_signature(void *); +extern uschar * tls_cert_signature_algorithm(void *); +extern uschar * tls_cert_subject(void *); +extern uschar * tls_cert_subject_altname(void *); +extern uschar * tls_cert_version(void *); + extern int tls_client_start(int, host_item *, address_item *, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *, # ifdef EXPERIMENTAL_OCSP @@ -32,9 +46,12 @@ extern int tls_client_start(int, host_item *, address_item *, # endif int, int, uschar *, uschar *); extern void tls_close(BOOL, BOOL); +extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); extern int tls_ferror(void); +extern void tls_free_cert(void *); extern int tls_getc(void); +extern int tls_import_cert(const uschar *, void **); extern int tls_read(BOOL, uschar *, size_t); extern int tls_server_start(const uschar *); extern BOOL tls_smtp_buffered(void); @@ -421,4 +438,6 @@ extern void version_init(void); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); +/* vi: aw +*/ /* End of functions.h */ diff --git a/src/src/globals.c b/src/src/globals.c index da81b8db5..7b591e42a 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -106,6 +106,8 @@ tls_support tls_in = { NULL, /* tls_cipher */ FALSE,/* tls_on_connect */ NULL, /* tls_on_connect_ports */ + NULL, /* tls_ourcert */ + NULL, /* tls_peercert */ NULL, /* tls_peerdn */ NULL /* tls_sni */ }; @@ -116,6 +118,8 @@ tls_support tls_out = { NULL, /* tls_cipher */ FALSE,/* tls_on_connect */ NULL, /* tls_on_connect_ports */ + NULL, /* tls_ourcert */ + NULL, /* tls_peercert */ NULL, /* tls_peerdn */ NULL /* tls_sni */ }; @@ -332,6 +336,8 @@ address_item address_defaults = { NULL, /* shadow_message */ #ifdef SUPPORT_TLS NULL, /* cipher */ + NULL, /* ourcert */ + NULL, /* peercert */ NULL, /* peerdn */ #endif NULL, /* authenticator */ diff --git a/src/src/globals.h b/src/src/globals.h index 79bf38caa..584d1bd09 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -85,6 +85,8 @@ typedef struct { uschar *cipher; /* Cipher used */ BOOL on_connect; /* For older MTAs that don't STARTTLS */ uschar *on_connect_ports; /* Ports always tls-on-connect */ + void *ourcert; /* Certificate we presented, binary */ + void *peercert; /* Certificate of peer, binary */ uschar *peerdn; /* DN from peer */ uschar *sni; /* Server Name Indication */ } tls_support; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 27ff13785..6810d25e3 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1818,6 +1818,8 @@ authenticated_by = NULL; #ifdef SUPPORT_TLS tls_in.cipher = tls_in.peerdn = NULL; +tls_in.ourcert = tls_in.peercert = NULL; +tls_in.sni = NULL; tls_advertised = FALSE; #endif diff --git a/src/src/spool_in.c b/src/src/spool_in.c index a546b6521..2006e1b02 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -285,6 +285,8 @@ dkim_collect_input = FALSE; #ifdef SUPPORT_TLS tls_in.certificate_verified = FALSE; tls_in.cipher = NULL; +tls_in.ourcert = NULL; +tls_in.peercert = NULL; tls_in.peerdn = NULL; tls_in.sni = NULL; #endif @@ -548,6 +550,12 @@ for (;;) tls_in.certificate_verified = TRUE; else if (Ustrncmp(p, "ls_cipher", 9) == 0) tls_in.cipher = string_copy(big_buffer + 12); +#ifndef COMPILE_UTILITY + else if (Ustrncmp(p, "ls_ourcert", 10) == 0) + (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert); + else if (Ustrncmp(p, "ls_peercert", 11) == 0) + (void) tls_import_cert(big_buffer + 14, &tls_in.peercert); +#endif else if (Ustrncmp(p, "ls_peerdn", 9) == 0) tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12)); else if (Ustrncmp(p, "ls_sni", 6) == 0) @@ -799,4 +807,6 @@ errno = ERRNO_SPOOLFORMAT; return inheader? spool_read_hdrerror : spool_read_enverror; } +/* vi: aw ai sw=2 +*/ /* End of spool_in.c */ diff --git a/src/src/spool_out.c b/src/src/spool_out.c index ce25a564e..7bbd42df0 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -229,9 +229,19 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts); #ifdef SUPPORT_TLS if (tls_in.certificate_verified) fprintf(f, "-tls_certificate_verified\n"); -if (tls_in.cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_in.cipher); -if (tls_in.peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_in.peerdn)); -if (tls_in.sni != NULL) fprintf(f, "-tls_sni %s\n", string_printing(tls_in.sni)); +if (tls_in.cipher) fprintf(f, "-tls_cipher %s\n", tls_in.cipher); +if (tls_in.peercert) + { + (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.peercert); + fprintf(f, "-tls_peercert %s\n", CS big_buffer); + } +if (tls_in.peerdn) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_in.peerdn)); +if (tls_in.sni) fprintf(f, "-tls_sni %s\n", string_printing(tls_in.sni)); +if (tls_in.ourcert) + { + (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.ourcert); + fprintf(f, "-tls_ourcert %s\n", CS big_buffer); + } #endif /* To complete the envelope, write out the tree of non-recipients, followed by diff --git a/src/src/structs.h b/src/src/structs.h index eb430851d..a6c78f4fc 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -540,6 +540,8 @@ typedef struct address_item { #ifdef SUPPORT_TLS uschar *cipher; /* Cipher used for transport */ + void *ourcert; /* Certificate offered to peer, binary */ + void *peercert; /* Certificate from peer, binary */ uschar *peerdn; /* DN of server's certificate */ #endif diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index ace59633a..880aaeb14 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -74,19 +74,20 @@ Not handled here: global tls_channelbinding_b64. */ typedef struct exim_gnutls_state { - gnutls_session_t session; + gnutls_session_t session; gnutls_certificate_credentials_t x509_cred; - gnutls_priority_t priority_cache; + gnutls_priority_t priority_cache; enum peer_verify_requirement verify_requirement; - int fd_in; - int fd_out; - BOOL peer_cert_verified; - BOOL trigger_sni_changes; - BOOL have_set_peerdn; + int fd_in; + int fd_out; + BOOL peer_cert_verified; + BOOL trigger_sni_changes; + BOOL have_set_peerdn; const struct host_item *host; - uschar *peerdn; - uschar *ciphersuite; - uschar *received_sni; + gnutls_x509_crt_t peercert; + uschar *peerdn; + uschar *ciphersuite; + uschar *received_sni; const uschar *tls_certificate; const uschar *tls_privatekey; @@ -288,12 +289,34 @@ tls_error(when, msg, state->host); * Set various Exim expansion vars * *************************************************/ +#define exim_gnutls_cert_err(Label) do { \ + if (rc != GNUTLS_E_SUCCESS) { \ + DEBUG(D_tls) debug_printf("TLS: cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \ + return rc; } } while (0) + +static int +import_cert(const gnutls_datum * cert, gnutls_x509_crt_t * crtp) +{ +int rc; + +rc = gnutls_x509_crt_init(crtp); +exim_gnutls_cert_err(US"gnutls_x509_crt_init (crt)"); + +rc = gnutls_x509_crt_import(*crtp, cert, GNUTLS_X509_FMT_DER); +exim_gnutls_cert_err(US"failed to import certificate [gnutls_x509_crt_import(cert)]"); + +return rc; +} + +#undef exim_gnutls_cert_err + + /* We set various Exim global variables from the state, once a session has been established. With TLS callouts, may need to change this to stack variables, or just re-call it with the server state after client callout has finished. -Make sure anything set here is inset in tls_getc(). +Make sure anything set here is unset in tls_getc(). Sets: tls_active fd @@ -301,15 +324,17 @@ Sets: tls_certificate_verified bool indicator tls_channelbinding_b64 for some SASL mechanisms tls_cipher a string + tls_peercert pointer to library internal tls_peerdn a string tls_sni a (UTF-8) string + tls_ourcert pointer to library internal Argument: state the relevant exim_gnutls_state_st * */ static void -extract_exim_vars_from_tls_state(exim_gnutls_state_st *state, BOOL is_server) +extract_exim_vars_from_tls_state(exim_gnutls_state_st * state) { gnutls_cipher_algorithm_t cipher; #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING @@ -317,18 +342,19 @@ int old_pool; int rc; gnutls_datum_t channel; #endif +tls_support * tlsp = state->tlsp; -state->tlsp->active = state->fd_out; +tlsp->active = state->fd_out; cipher = gnutls_cipher_get(state->session); /* returns size in "bytes" */ -state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; +tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; -state->tlsp->cipher = state->ciphersuite; +tlsp->cipher = state->ciphersuite; DEBUG(D_tls) debug_printf("cipher: %s\n", state->ciphersuite); -state->tlsp->certificate_verified = state->peer_cert_verified; +tlsp->certificate_verified = state->peer_cert_verified; /* note that tls_channelbinding_b64 is not saved to the spool file, since it's only available for use for authenticators while this TLS session is running. */ @@ -349,8 +375,17 @@ if (rc) { } #endif -state->tlsp->peerdn = state->peerdn; -state->tlsp->sni = state->received_sni; +/* peercert is set in peer_status() */ +tlsp->peerdn = state->peerdn; +tlsp->sni = state->received_sni; + +/* record our certificate */ + { + const gnutls_datum * cert = gnutls_certificate_get_ours(state->session); + gnutls_x509_crt_t crt; + + tlsp->ourcert = cert && import_cert(cert, &crt)==0 ? crt : NULL; + } } @@ -1099,7 +1134,6 @@ return OK; - /************************************************* * Extract peer information * *************************************************/ @@ -1205,11 +1239,11 @@ if (ct != GNUTLS_CRT_X509) if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \ return OK; } } while (0) -rc = gnutls_x509_crt_init(&crt); -exim_gnutls_peer_err(US"gnutls_x509_crt_init (crt)"); +rc = import_cert(&cert_list[0], &crt); +exim_gnutls_peer_err(US"cert 0"); + +state->tlsp->peercert = state->peercert = crt; -rc = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER); -exim_gnutls_peer_err(US"failed to import certificate [gnutls_x509_crt_import(cert 0)]"); sz = 0; rc = gnutls_x509_crt_get_dn(crt, NULL, &sz); if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) @@ -1220,6 +1254,7 @@ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) dn_buf = store_get_perm(sz); rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz); exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]"); + state->peerdn = dn_buf; return OK; @@ -1484,7 +1519,7 @@ mode, the fflush() happens when smtp_getc() is called. */ if (!state->tlsp->on_connect) { smtp_printf("220 TLS go ahead\r\n"); - fflush(smtp_out); /*XXX JGH */ + fflush(smtp_out); } /* Now negotiate the TLS session. We put our own timer on it, since it seems @@ -1526,22 +1561,17 @@ DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); /* Verify after the fact */ -if (state->verify_requirement != VERIFY_NONE) +if ( state->verify_requirement != VERIFY_NONE + && !verify_certificate(state, &error)) { - if (!verify_certificate(state, &error)) + if (state->verify_requirement != VERIFY_OPTIONAL) { - if (state->verify_requirement == VERIFY_OPTIONAL) - { - DEBUG(D_tls) - debug_printf("TLS: continuing on only because verification was optional, after: %s\n", - error); - } - else - { - tls_error(US"certificate verification failed", error, NULL); - return FAIL; - } + tls_error(US"certificate verification failed", error, NULL); + return FAIL; } + DEBUG(D_tls) + debug_printf("TLS: continuing on only because verification was optional, after: %s\n", + error); } /* Figure out peer DN, and if authenticated, etc. */ @@ -1551,7 +1581,7 @@ if (rc != OK) return rc; /* Sets various Exim expansion variables; always safe within server */ -extract_exim_vars_from_tls_state(state, TRUE); +extract_exim_vars_from_tls_state(state); /* TLS has been set up. Adjust the input functions to read via TLS, and initialize appropriately. */ @@ -1664,16 +1694,22 @@ else } #ifdef EXPERIMENTAL_OCSP /* since GnuTLS 3.1.3 */ -if (require_ocsp && - (rc = gnutls_ocsp_status_request_enable_client(state->session, NULL, 0, NULL)) - != OK) - return tls_error(US"cert-status-req", gnutls_strerror(rc), state->host); +if (require_ocsp) + { + DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n"); + rc = gnutls_ocsp_status_request_enable_client(state->session, + NULL, 0, NULL); + if (rc != OK) + return tls_error(US"cert-status-req", + gnutls_strerror(rc), state->host); + } #endif gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd); state->fd_in = fd; state->fd_out = fd; +DEBUG(D_tls) debug_printf("about to gnutls_handshake\n"); /* There doesn't seem to be a built-in timeout on connection. */ sigalrm_seen = FALSE; @@ -1732,7 +1768,7 @@ if ((rc = peer_status(state)) != OK) /* Sets various Exim expansion variables; may need to adjust for ACL callouts */ -extract_exim_vars_from_tls_state(state, FALSE); +extract_exim_vars_from_tls_state(state); return OK; } @@ -1830,8 +1866,9 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) state->tlsp->active = -1; state->tlsp->bits = 0; state->tlsp->certificate_verified = FALSE; - tls_channelbinding_b64 = NULL; /*XXX JGH */ + tls_channelbinding_b64 = NULL; state->tlsp->cipher = NULL; + state->tlsp->peercert = NULL; state->tlsp->peerdn = NULL; return smtp_getc(); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index bdf910acc..2f08e43c6 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -276,7 +276,11 @@ if (state == 0) txt); tlsp->certificate_verified = FALSE; *calledp = TRUE; - if (!*optionalp) return 0; /* reject */ + if (!*optionalp) + { + tlsp->peercert = X509_dup(x509ctx->current_cert); + return 0; /* reject */ + } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); return 1; /* accept */ @@ -303,6 +307,7 @@ else DEBUG(D_tls) debug_printf("SSL%s peer: %s\n", *calledp ? "" : " authenticated", txt); tlsp->peerdn = txt; + tlsp->peercert = X509_dup(x509ctx->current_cert); } /*XXX JGH: this looks bogus - we set "verified" first time through, which @@ -1433,6 +1438,11 @@ DEBUG(D_tls) debug_printf("Shared ciphers: %s\n", buf); } +/* Record the certificate we presented */ + { + X509 * crt = SSL_get_certificate(server_ssl); + tls_in.ourcert = crt ? X509_dup(crt) : NULL; + } /* Only used by the server-side tls (tls_in), including tls_getc. Client-side (tls_out) reads (seem to?) go via @@ -1597,12 +1607,13 @@ if (rc <= 0) DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); /* Beware anonymous ciphers which lead to server_cert being NULL */ +/*XXX server_cert is never freed... use X509_free() */ server_cert = SSL_get_peer_certificate (client_ssl); if (server_cert) { tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert), CS txt, sizeof(txt)); - tls_out.peerdn = txt; + tls_out.peerdn = txt; /*XXX a static buffer... */ } else tls_out.peerdn = NULL; @@ -1610,6 +1621,12 @@ else construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits); tls_out.cipher = cipherbuf; +/* Record the certificate we presented */ + { + X509 * crt = SSL_get_certificate(client_ssl); + tls_out.ourcert = crt ? X509_dup(crt) : NULL; + } + tls_out.active = fd; return OK; } @@ -2250,4 +2267,6 @@ for (s=option_spec; *s != '\0'; /**/) return TRUE; } +/* vi: aw ai sw=2 +*/ /* End of tls-openssl.c */ diff --git a/src/src/tls.c b/src/src/tls.c index 972785284..ad7fe609c 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -85,6 +85,7 @@ return TRUE; #ifdef USE_GNUTLS #include "tls-gnu.c" +#include "tlscert-gnu.c" #define ssl_xfer_buffer (state_server.xfer_buffer) #define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm) @@ -94,6 +95,7 @@ return TRUE; #else #include "tls-openssl.c" +#include "tlscert-openssl.c" #endif diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c new file mode 100644 index 000000000..649e93a35 --- /dev/null +++ b/src/src/tlscert-gnu.c @@ -0,0 +1,325 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2014 */ + +/* This file provides TLS/SSL support for Exim using the GnuTLS library, +one of the available supported implementations. This file is #included into +tls.c when USE_GNUTLS has been set. +*/ + +#include +/* needed for cert checks in verification and DN extraction: */ +#include +/* needed to disable PKCS11 autoload unless requested */ +#if GNUTLS_VERSION_NUMBER >= 0x020c00 +# include +#endif + + +/***************************************************** +* Export/import a certificate, binary/printable +*****************************************************/ +int +tls_export_cert(uschar * buf, size_t buflen, void * cert) +{ +size_t sz = buflen; +void * reset_point = store_get(0); +int fail = 0; +uschar * cp; + +if (gnutls_x509_crt_export((gnutls_x509_crt_t)cert, + GNUTLS_X509_FMT_PEM, buf, &sz)) + return 1; +if ((cp = string_printing(buf)) != buf) + { + Ustrncpy(buf, cp, buflen); + if (buf[buflen-1]) + fail = 1; + } +store_reset(reset_point); +return fail; +} + +int +tls_import_cert(const uschar * buf, void ** cert) +{ +void * reset_point = store_get(0); +gnutls_datum_t datum; +gnutls_x509_crt_t crt; +int fail = 0; + +gnutls_global_init(); +gnutls_x509_crt_init(&crt); + +datum.data = string_unprinting(US buf); +datum.size = Ustrlen(datum.data); +if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM)) + fail = 1; +else + *cert = (void *)crt; + +store_reset(reset_point); +return fail; +} + +void +tls_free_cert(void * cert) +{ +gnutls_x509_crt_deinit((gnutls_x509_crt_t) cert); +gnutls_global_deinit(); +} + +/***************************************************** +* Certificate field extraction routines +*****************************************************/ +static uschar * +time_copy(time_t t) +{ +uschar * cp = store_get(32); +struct tm * tp = gmtime(&t); +size_t len = strftime(CS cp, 32, "%b %e %T %Y %Z", tp); +return len > 0 ? cp : NULL; +} + +/**/ + +uschar * +tls_cert_issuer(void * cert) +{ +uschar txt[256]; +size_t sz = sizeof(txt); +return ( gnutls_x509_crt_get_issuer_dn(cert, CS txt, &sz) == 0 ) + ? string_copy(txt) : NULL; +} + +uschar * +tls_cert_not_after(void * cert) +{ +return time_copy( + gnutls_x509_crt_get_expiration_time((gnutls_x509_crt_t)cert)); +} + +uschar * +tls_cert_not_before(void * cert) +{ +return time_copy( + gnutls_x509_crt_get_activation_time((gnutls_x509_crt_t)cert)); +} + +uschar * +tls_cert_serial_number(void * cert) +{ +uschar bin[50], txt[150]; +size_t sz = sizeof(bin); +uschar * sp; +uschar * dp; + +if (gnutls_x509_crt_get_serial((gnutls_x509_crt_t)cert, + bin, &sz) || sz > sizeof(bin)) + return NULL; +for(dp = txt, sp = bin; sz; dp += 2, sp++, sz--) + sprintf(dp, "%.2x", *sp); +for(sp = txt; sp[0]=='0' && sp[1]; ) sp++; /* leading zeroes */ +return string_copy(sp); +} + +uschar * +tls_cert_signature(void * cert) +{ +uschar * cp1; +uschar * cp2; +uschar * cp3; +size_t len = 0; +int ret; + +if ((ret = gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len)) != + GNUTLS_E_SHORT_MEMORY_BUFFER) + { + fprintf(stderr, "%s: gs0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret)); + return NULL; + } + +cp1 = store_get(len*4+1); + +if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len) != 0) + { + fprintf(stderr, "%s: gs1 fail\n", __FUNCTION__); + return NULL; + } + +for(cp3 = cp2 = cp1+len; cp1 < cp2; cp3 += 3, cp1++) + sprintf(cp3, "%.2x ", *cp1); +cp3[-1]= '\0'; + +return cp2; +} + +uschar * +tls_cert_signature_algorithm(void * cert) +{ +gnutls_sign_algorithm_t algo = + gnutls_x509_crt_get_signature_algorithm((gnutls_x509_crt_t)cert); +return algo < 0 ? NULL : string_copy(gnutls_sign_get_name(algo)); +} + +uschar * +tls_cert_subject(void * cert) +{ +static uschar txt[256]; +size_t sz = sizeof(txt); +return ( gnutls_x509_crt_get_dn(cert, CS txt, &sz) == 0 ) + ? string_copy(txt) : NULL; +} + +uschar * +tls_cert_version(void * cert) +{ +return string_sprintf("%d", gnutls_x509_crt_get_version(cert)); +} + +uschar * +tls_cert_ext_by_oid(void * cert, uschar * oid, int idx) +{ +uschar * cp1 = NULL; +uschar * cp2; +uschar * cp3; +size_t siz = 0; +unsigned int crit; +int ret; + +ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert, + oid, idx, cp1, &siz, &crit); +if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + fprintf(stderr, "%s: ge0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret)); + return NULL; + } + +cp1 = store_get(siz*4 + 1); + +ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert, + oid, idx, cp1, &siz, &crit); +if (ret < 0) + { + fprintf(stderr, "%s: ge1 fail: %s\n", __FUNCTION__, gnutls_strerror(ret)); + return NULL; + } + +/* binary data, DER encoded */ + +/* just dump for now */ +for(cp3 = cp2 = cp1+siz; cp1 < cp2; cp3 += 3, cp1++) + sprintf(cp3, "%.2x ", *cp1); +cp3[-1]= '\0'; + +return cp2; +} + +uschar * +tls_cert_subject_altname(void * cert) +{ +uschar * cp = NULL; +size_t siz = 0; +unsigned int crit; +int ret; + +ret = gnutls_x509_crt_get_subject_alt_name ((gnutls_x509_crt_t)cert, + 0, cp, &siz, &crit); +switch(ret) + { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + return NULL; + case GNUTLS_E_SHORT_MEMORY_BUFFER: + break; + default: + expand_string_message = + string_sprintf("%s: gs0 fail: %d %s\n", __FUNCTION__, + ret, gnutls_strerror(ret)); + return NULL; + } + +cp = store_get(siz+1); +ret = gnutls_x509_crt_get_subject_alt_name ((gnutls_x509_crt_t)cert, + 0, cp, &siz, &crit); +if (ret < 0) + { + expand_string_message = + string_sprintf("%s: gs1 fail: %d %s\n", __FUNCTION__, + ret, gnutls_strerror(ret)); + return NULL; + } +cp[siz] = '\0'; +return cp; +} + +uschar * +tls_cert_ocsp_uri(void * cert) +{ +#if GNUTLS_VERSION_NUMBER >= 0x030000 +gnutls_datum_t uri; +unsigned int crit; +int ret = gnutls_x509_crt_get_authority_info_access((gnutls_x509_crt_t)cert, + 0, GNUTLS_IA_OCSP_URI, &uri, &crit); + +if (ret >= 0) + return string_copyn(uri.data, uri.size); + +if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + expand_string_message = + string_sprintf("%s: gai fail: %d %s\n", __FUNCTION__, + ret, gnutls_strerror(ret)); + +return NULL; + +#else + +expand_string_message = + string_sprintf("%s: OCSP support with GnuTLS requires version 3.0.0\n", + __FUNCTION__); +return NULL; + +#endif +} + +uschar * +tls_cert_crl_uri(void * cert) +{ +int ret; +uschar * cp = NULL; +size_t siz = 0; + +ret = gnutls_x509_crt_get_crl_dist_points ((gnutls_x509_crt_t)cert, + 0, cp, &siz, NULL, NULL); +switch(ret) + { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + return NULL; + case GNUTLS_E_SHORT_MEMORY_BUFFER: + break; + default: + expand_string_message = + string_sprintf("%s: gc0 fail: %d %s\n", __FUNCTION__, + ret, gnutls_strerror(ret)); + return NULL; + } + +cp = store_get(siz+1); +ret = gnutls_x509_crt_get_crl_dist_points ((gnutls_x509_crt_t)cert, + 0, cp, &siz, NULL, NULL); +if (ret < 0) + { + expand_string_message = + string_sprintf("%s: gs1 fail: %d %s\n", __FUNCTION__, + ret, gnutls_strerror(ret)); + return NULL; + } +cp[siz] = '\0'; +return cp; +} + + +/* vi: aw ai sw=2 +*/ +/* End of tlscert-gnu.c */ diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c new file mode 100644 index 000000000..008cf54b9 --- /dev/null +++ b/src/src/tlscert-openssl.c @@ -0,0 +1,293 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2014 */ + +/* This module provides TLS (aka SSL) support for Exim using the OpenSSL +library. It is #included into the tls.c file when that library is used. +*/ + + +/* Heading stuff */ + +#include +#include +#include +#include + + +/***************************************************** +* Export/import a certificate, binary/printable +*****************************************************/ +int +tls_export_cert(uschar * buf, size_t buflen, void * cert) +{ +BIO * bp = BIO_new(BIO_s_mem()); +int fail; + +if ((fail = PEM_write_bio_X509(bp, (X509 *)cert) ? 0 : 1)) + log_write(0, LOG_MAIN, "TLS error in certificate export: %s", + ERR_error_string(ERR_get_error(), NULL)); +else + { + char * cp = CS buf; + int n; + buflen -= 2; + for(;;) + { + if ((n = BIO_gets(bp, cp, (int)buflen)) <= 0) break; + cp += n+1; + buflen -= n+1; + cp[-2] = '\\'; cp[-1] = 'n'; /* newline->"\n" */ + } /* compat with string_printing() */ + *cp = '\0'; + } + +BIO_free(bp); +return fail; +} + +int +tls_import_cert(const uschar * buf, void ** cert) +{ +void * reset_point = store_get(0); +const uschar * cp = string_unprinting(US buf); +BIO * bp; +X509 * x; + +bp = BIO_new_mem_buf(US cp, -1); +x = PEM_read_bio_X509(bp, NULL, 0, NULL); +int fail = 0; +if (!x) + fail = 1; +else + *cert = (void *)x; +BIO_free(bp); +store_reset(reset_point); +return fail; +} + +void +tls_free_cert(void * cert) +{ +X509_free((X509 *)cert); +} + +/***************************************************** +* Certificate field extraction routines +*****************************************************/ +static uschar * +bio_string_copy(BIO * bp, int len) +{ +uschar * cp = ""; +len = len > 0 ? (int) BIO_get_mem_data(bp, &cp) : 0; +cp = string_copyn(cp, len); +BIO_free(bp); +return cp; +} + +static uschar * +asn1_time_copy(const ASN1_TIME * time) +{ +BIO * bp = BIO_new(BIO_s_mem()); +int len = ASN1_TIME_print(bp, time); +return bio_string_copy(bp, len); +} + +static uschar * +x509_name_copy(X509_NAME * name) +{ +BIO * bp = BIO_new(BIO_s_mem()); +int len_good = + X509_NAME_print_ex(bp, name, 0, XN_FLAG_RFC2253) >= 0 + ? 1 : 0; +return bio_string_copy(bp, len_good); +} + +/**/ + +uschar * +tls_cert_issuer(void * cert) +{ +return x509_name_copy(X509_get_issuer_name((X509 *)cert)); +} + +uschar * +tls_cert_not_before(void * cert) +{ +return asn1_time_copy(X509_get_notBefore((X509 *)cert)); +} + +uschar * +tls_cert_not_after(void * cert) +{ +return asn1_time_copy(X509_get_notAfter((X509 *)cert)); +} + +uschar * +tls_cert_serial_number(void * cert) +{ +uschar txt[256]; +BIO * bp = BIO_new(BIO_s_mem()); +int len = i2a_ASN1_INTEGER(bp, X509_get_serialNumber((X509 *)cert)); + +if (len < sizeof(txt)) + BIO_read(bp, txt, len); +else + len = 0; +BIO_free(bp); +return string_copynlc(txt, len); /* lowercase */ +} + +uschar * +tls_cert_signature(void * cert) +{ +BIO * bp = BIO_new(BIO_s_mem()); +uschar * cp = NULL; + +if (X509_print_ex(bp, (X509 *)cert, 0, + X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | + X509_FLAG_NO_SIGNAME | X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | + X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | + /* X509_FLAG_NO_SIGDUMP is the missing one */ + X509_FLAG_NO_AUX) == 1) + { + long len = BIO_get_mem_data(bp, &cp); + cp = string_copyn(cp, len); + } +BIO_free(bp); +return cp; +} + +uschar * +tls_cert_signature_algorithm(void * cert) +{ +return string_copy(OBJ_nid2ln(X509_get_signature_type((X509 *)cert))); +} + +uschar * +tls_cert_subject(void * cert) +{ +return x509_name_copy(X509_get_subject_name((X509 *)cert)); +} + +uschar * +tls_cert_version(void * cert) +{ +return string_sprintf("%d", X509_get_version((X509 *)cert)); +} + +uschar * +tls_cert_ext_by_oid(void * cert, uschar * oid, int idx) +{ +int nid = OBJ_create(oid, "", ""); +int nidx = X509_get_ext_by_NID((X509 *)cert, nid, idx); +X509_EXTENSION * ex = X509_get_ext((X509 *)cert, nidx); +ASN1_OCTET_STRING * adata = X509_EXTENSION_get_data(ex); +BIO * bp = BIO_new(BIO_s_mem()); +long len; +uschar * cp1; +uschar * cp2; +uschar * cp3; + +M_ASN1_OCTET_STRING_print(bp, adata); +/* binary data, DER encoded */ + +/* just dump for now */ +len = BIO_get_mem_data(bp, &cp1); +cp3 = cp2 = store_get(len*3+1); + +while(len) + { + sprintf(cp2, "%.2x ", *cp1++); + cp2 += 3; + len--; + } +cp2[-1] = '\0'; + +return cp3; +} + +uschar * +tls_cert_subject_altname(void * cert) +{ +uschar * cp; +STACK_OF(GENERAL_NAME) * san = (STACK_OF(GENERAL_NAME) *) + X509_get_ext_d2i((X509 *)cert, NID_subject_alt_name, NULL, NULL); + +if (!san) return NULL; + +while (sk_GENERAL_NAME_num(san) > 0) + { + GENERAL_NAME * namePart = sk_GENERAL_NAME_pop(san); + switch (namePart->type) + { + case GEN_URI: + cp = string_sprintf("URI=%s", + ASN1_STRING_data(namePart->d.uniformResourceIdentifier)); + return cp; + case GEN_EMAIL: + cp = string_sprintf("email=%s", + ASN1_STRING_data(namePart->d.rfc822Name)); + return cp; + default: + cp = string_sprintf("Unrecognisable"); + return cp; + } + } + +/* sk_GENERAL_NAME_pop_free(gen_names, GENERAL_NAME_free); ??? */ +return cp; +} + +uschar * +tls_cert_ocsp_uri(void * cert) +{ +STACK_OF(ACCESS_DESCRIPTION) * ads = (STACK_OF(ACCESS_DESCRIPTION) *) + X509_get_ext_d2i((X509 *)cert, NID_info_access, NULL, NULL); +int adsnum = sk_ACCESS_DESCRIPTION_num(ads); +int i; + +for (i = 0; i < adsnum; i++) + { + ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(ads, i); + + if (ad && OBJ_obj2nid(ad->method) == NID_ad_OCSP) + return string_copy( ASN1_STRING_data(ad->location->d.ia5) ); + } + +return NULL; +} + +uschar * +tls_cert_crl_uri(void * cert) +{ +STACK_OF(DIST_POINT) * dps = (STACK_OF(DIST_POINT) *) + X509_get_ext_d2i((X509 *)cert, NID_crl_distribution_points, + NULL, NULL); +DIST_POINT * dp; +int dpsnum = sk_DIST_POINT_num(dps); +int i; + +if (dps) for (i = 0; i < dpsnum; i++) + if ((dp = sk_DIST_POINT_value(dps, i))) + { + STACK_OF(GENERAL_NAME) * names = dp->distpoint->name.fullname; + GENERAL_NAME * np; + int nnum = sk_GENERAL_NAME_num(names); + int j; + + for (j = 0; j < nnum; j++) + if ( (np = sk_GENERAL_NAME_value(names, j)) + && np->type == GEN_URI + ) + return string_copy(ASN1_STRING_data( + np->d.uniformResourceIdentifier)); + } +return NULL; +} + +/* vi: aw ai sw=2 +*/ +/* End of tlscert-openssl.c */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 9e0ab1556..16c2b6011 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1221,6 +1221,8 @@ outblock.authenticating = FALSE; tls_out.bits = 0; tls_out.cipher = NULL; /* the one we may use for this transport */ +tls_out.ourcert = NULL; +tls_out.peercert = NULL; tls_out.peerdn = NULL; #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) tls_out.sni = NULL; @@ -1480,6 +1482,8 @@ if (tls_offered && !suppress_tls && if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; + addr->ourcert = tls_out.ourcert; + addr->peercert = tls_out.peercert; addr->peerdn = tls_out.peerdn; } } @@ -2417,8 +2421,6 @@ tls_close(FALSE, TRUE); #endif /* Close the socket, and return the appropriate value, first setting -continue_transport and continue_hostname NULL to prevent any other addresses -that may include the host from trying to re-use a continuation socket. This works because the NULL setting is passed back to the calling process, and remote_max_parallel is forced to 1 when delivering over an existing connection, @@ -2519,6 +2521,8 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->message = NULL; #ifdef SUPPORT_TLS addr->cipher = NULL; + addr->ourcert = NULL; + addr->peercert = NULL; addr->peerdn = NULL; #endif } diff --git a/test/confs/2002 b/test/confs/2002 index e8358da25..b4d0348ca 100644 --- a/test/confs/2002 +++ b/test/confs/2002 @@ -20,11 +20,11 @@ queue_run_in_order tls_advertise_hosts = 127.0.0.1 : HOSTIPV4 -tls_certificate = DIR/aux-fixed/cert1 -tls_privatekey = DIR/aux-fixed/cert1 +tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem +tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key tls_verify_hosts = HOSTIPV4 -tls_verify_certificates = DIR/aux-fixed/cert2 +tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem # ------ ACL ------ @@ -41,7 +41,23 @@ check_recipient: DHE_RSA_AES_256_CBC_SHA1 : \ DHE_RSA_3DES_EDE_CBC_SHA : \ RSA_AES_256_CBC_SHA1 - accept + warn logwrite = ${if def:tls_in_ourcert \ + {Our cert SN: <${certextract{subject}{$tls_in_ourcert}}>} \ + {We did not present a cert}} + accept condition = ${if !def:tls_in_peercert} + logwrite = Peer did not present a cert + accept logwrite = Peer cert: + logwrite = ver ${certextract {version}{$tls_in_peercert}} + logwrite = SR <${certextract {serial_number}{$tls_in_peercert}}> + logwrite = SN <${certextract {subject} {$tls_in_peercert}}> + logwrite = IN <${certextract {issuer} {$tls_in_peercert}}> + logwrite = NB <${certextract {notbefore} {$tls_in_peercert}}> + logwrite = NA <${certextract {notafter} {$tls_in_peercert}}> + logwrite = SA <${certextract {signature_algorithm}{$tls_in_peercert}}> + logwrite = SG <${certextract {signature} {$tls_in_peercert}}> + logwrite = ${certextract {subject_altname}{$tls_in_peercert} {SAN <$value>}{(no SAN)}} +# logwrite = ${certextract {ocsp_uri} {$tls_in_peercert} {OCU <$value>}{(no OCU)}} + logwrite = ${certextract {crl_uri} {$tls_in_peercert} {CRU <$value>}{(no CRU)}} # ----- Routers ----- diff --git a/test/confs/2102 b/test/confs/2102 index 7f5771c0e..5332801dc 100644 --- a/test/confs/2102 +++ b/test/confs/2102 @@ -20,11 +20,11 @@ queue_run_in_order tls_advertise_hosts = 127.0.0.1 : HOSTIPV4 -tls_certificate = DIR/aux-fixed/cert1 -tls_privatekey = DIR/aux-fixed/cert1 +tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem +tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key tls_verify_hosts = HOSTIPV4 -tls_verify_certificates = DIR/aux-fixed/cert2 +tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem # ------ ACL ------ @@ -42,7 +42,23 @@ check_recipient: DHE-RSA-AES256-GCM-SHA384 : \ DHE_RSA_AES_256_CBC_SHA1 : \ DHE_RSA_3DES_EDE_CBC_SHA - accept + warn logwrite = ${if def:tls_in_ourcert \ + {Our cert SN: <${certextract{subject}{$tls_in_ourcert}}>} \ + {We did not present a cert}} + accept condition = ${if !def:tls_in_peercert} + logwrite = Peer did not present a cert + accept logwrite = Peer cert: + logwrite = ver ${certextract {version}{$tls_in_peercert}} + logwrite = SR <${certextract {serial_number}{$tls_in_peercert}}> + logwrite = SN <${certextract {subject} {$tls_in_peercert}}> + logwrite = IN <${certextract {issuer} {$tls_in_peercert}}> + logwrite = NB <${certextract {notbefore} {$tls_in_peercert}}> + logwrite = NA <${certextract {notafter} {$tls_in_peercert}}> + logwrite = SA <${certextract {signature_algorithm}{$tls_in_peercert}}> + logwrite = SG <${certextract {signature} {$tls_in_peercert}}> + logwrite = ${certextract {subject_altname}{$tls_in_peercert} {SAN <$value>}{(no SAN)}} + logwrite = ${certextract {ocsp_uri} {$tls_in_peercert} {OCU <$value>}{(no OCU)}} + logwrite = ${certextract {crl_uri} {$tls_in_peercert} {CRU <$value>}{(no CRU)}} # ----- Routers ----- diff --git a/test/confs/5750 b/test/confs/5750 new file mode 100644 index 000000000..a4762bd19 --- /dev/null +++ b/test/confs/5750 @@ -0,0 +1,95 @@ +# Exim test configuration 5750 (dup of 5760) +# $tls_out_peercert - GnuTLS + +SERVER= + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +rfc1413_query_timeout = 0s +spool_directory = DIR/spool +log_file_path = DIR/spool/log/SERVER%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +acl_smtp_rcpt = accept + +log_selector = +tls_peerdn + +queue_only +queue_run_in_order + +tls_advertise_hosts = * + +tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem +tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key + +tls_verify_hosts = * +tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem + +# + +begin acl +logger: + warn logwrite = $acl_arg1 $tpda_delivery_local_part + warn logwrite = ${if !def:tls_out_ourcert \ + {NO CLENT CERT presented} \ + {Our cert SN: ${certextract{subject}{$tls_out_ourcert}}}} + accept condition = ${if !def:tls_out_peercert} + logwrite = No Peer cert + accept logwrite = Peer cert: + logwrite = ver <${certextract {version} {$tls_out_peercert}}> + logwrite = SN <${certextract {subject} {$tls_out_peercert}}> + logwrite = IN <${certextract {issuer} {$tls_out_peercert}}> + logwrite = NB <${certextract {notbefore} {$tls_out_peercert}}> + logwrite = NA <${certextract {notafter} {$tls_out_peercert}}> + logwrite = SA <${certextract {signature_algorithm}{$tls_out_peercert}}> + logwrite = SG <${certextract {signature} {$tls_out_peercert}}> + logwrite = ${certextract {subject_altname}{$tls_out_peercert}{SAN <$value>}{(no SAN)}} + logwrite = ${certextract {ocsp_uri} {$tls_out_peercert} {OCU <$value>}{(no OCU)}} + logwrite = ${certextract {crl_uri} {$tls_out_peercert} {CRU <$value>}{(no CRU)}} + + +# ----- Routers ----- + +begin routers + +client: + driver = accept + condition = ${if eq {SERVER}{server}{no}{yes}} + retry_use_local_part + transport = send_to_server + + +# ----- Transports ----- + +begin transports + +send_to_server: + driver = smtp + allow_localhost + hosts = 127.0.0.1 + port = PORT_D + + tls_certificate = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem + tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key + + tls_verify_certificates = DIR/aux-fixed/exim-ca/\ + ${if eq {$local_part}{good}\ +{example.com/server1.example.com/ca_chain.pem}\ +{example.net/server1.example.net/ca_chain.pem}} + + tpda_delivery_action = ${acl {logger} {delivery} {$domain} } + tpda_host_defer_action = ${acl {logger} {deferral} {$domain} } + +# ----- Retry ----- + + +begin retry + +* * F,5d,10s + + +# End diff --git a/test/confs/5760 b/test/confs/5760 new file mode 100644 index 000000000..0e11ab0d3 --- /dev/null +++ b/test/confs/5760 @@ -0,0 +1,95 @@ +# Exim test configuration 5760 (dup of 5750) +# $tls_out_peercert - OpenSSL + +SERVER= + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +rfc1413_query_timeout = 0s +spool_directory = DIR/spool +log_file_path = DIR/spool/log/SERVER%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +acl_smtp_rcpt = accept + +log_selector = +tls_peerdn + +queue_only +queue_run_in_order + +tls_advertise_hosts = * + +tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem +tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key + +tls_verify_hosts = * +tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem + +# + +begin acl +logger: + warn logwrite = $acl_arg1 $tpda_delivery_local_part + warn logwrite = ${if !def:tls_out_ourcert \ + {NO CLENT CERT presented} \ + {Our cert SN: ${certextract{subject}{$tls_out_ourcert}}}} + accept condition = ${if !def:tls_out_peercert} + logwrite = No Peer cert + accept logwrite = Peer cert: + logwrite = ver <${certextract {version} {$tls_out_peercert}}> + logwrite = SN <${certextract {subject} {$tls_out_peercert}}> + logwrite = IN <${certextract {issuer} {$tls_out_peercert}}> + logwrite = NB <${certextract {notbefore} {$tls_out_peercert}}> + logwrite = NA <${certextract {notafter} {$tls_out_peercert}}> + logwrite = SA <${certextract {signature_algorithm}{$tls_out_peercert}}> + logwrite = SG <${certextract {signature} {$tls_out_peercert}}> + logwrite = ${certextract {subject_altname}{$tls_out_peercert}{SAN <$value>}{(no SAN)}} + logwrite = ${certextract {ocsp_uri} {$tls_out_peercert} {OCU <$value>}{(no OCU)}} + logwrite = ${certextract {crl_uri} {$tls_out_peercert} {CRU <$value>}{(no CRU)}} + + +# ----- Routers ----- + +begin routers + +client: + driver = accept + condition = ${if eq {SERVER}{server}{no}{yes}} + retry_use_local_part + transport = send_to_server + + +# ----- Transports ----- + +begin transports + +send_to_server: + driver = smtp + allow_localhost + hosts = 127.0.0.1 + port = PORT_D + + tls_certificate = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem + tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key + + tls_verify_certificates = DIR/aux-fixed/exim-ca/\ + ${if eq {$local_part}{good}\ +{example.com/server1.example.com/ca_chain.pem}\ +{example.net/server1.example.net/ca_chain.pem}} + + tpda_delivery_action = ${acl {logger} {delivery} {$domain} } + tpda_host_defer_action = ${acl {logger} {deferral} {$domain} } + +# ----- Retry ----- + + +begin retry + +* * F,5d,10s + + +# End diff --git a/test/log/2002 b/test/log/2002 index 774495514..e2777b459 100644 --- a/test/log/2002 +++ b/test/log/2002 @@ -1,8 +1,24 @@ 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer did not present a cert 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer did not present a cert 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (gnutls_handshake): The peer did not send any certificate. -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer cert: +1999-03-02 09:44:33 ver 3 +1999-03-02 09:44:33 SR +1999-03-02 09:44:33 SN +1999-03-02 09:44:33 IN +1999-03-02 09:44:33 NB +1999-03-02 09:44:33 NA +1999-03-02 09:44:33 SA +1999-03-02 09:44:33 SG <6c 37 41 26 4d 5d f4 b5 31 10 67 ca fb 64 b6 22 98 62 f7 1e 95 7b 6c e6 74 47 21 f4 5e 89 36 3e b9 9c 8a c5 52 bb c4 af 12 93 26 3b d7 3d e0 56 71 1e 1d 21 20 02 ed f0 4e d5 5e 45 42 fd 3c 38 41 54 83 86 0b 3b bf c5 47 39 ff 15 ea 93 dc fd c7 3d 18 58 59 ca dd 2a d8 b9 f9 2f b9 76 93 f4 ae e3 91 56 80 2f 8c 04 2f ad 57 ef d2 51 19 f4 b4 ef 32 9c ac 3a 7c 0d b8 39 db b1 e3 30 73 1a> +1999-03-02 09:44:33 SAN +1999-03-02 09:44:33 CRU +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server2.example.com" S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER R=abc T=local_delivery 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed diff --git a/test/log/2102 b/test/log/2102 index da4ee49d7..6e0713f41 100644 --- a/test/log/2102 +++ b/test/log/2102 @@ -1,9 +1,26 @@ 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer did not present a cert 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer did not present a cert 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (SSL_accept): error: <> 1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?) -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLSv1:AES256-SHA:256 DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss +1999-03-02 09:44:33 Our cert SN: +1999-03-02 09:44:33 Peer cert: +1999-03-02 09:44:33 ver 2 +1999-03-02 09:44:33 SR +1999-03-02 09:44:33 SN +1999-03-02 09:44:33 IN +1999-03-02 09:44:33 NB +1999-03-02 09:44:33 NA +1999-03-02 09:44:33 SA +1999-03-02 09:44:33 SG < Signature Algorithm: sha1WithRSAEncryption\n 6c:37:41:26:4d:5d:f4:b5:31:10:67:ca:fb:64:b6:22:98:62:\n f7:1e:95:7b:6c:e6:74:47:21:f4:5e:89:36:3e:b9:9c:8a:c5:\n 52:bb:c4:af:12:93:26:3b:d7:3d:e0:56:71:1e:1d:21:20:02:\n ed:f0:4e:d5:5e:45:42:fd:3c:38:41:54:83:86:0b:3b:bf:c5:\n 47:39:ff:15:ea:93:dc:fd:c7:3d:18:58:59:ca:dd:2a:d8:b9:\n f9:2f:b9:76:93:f4:ae:e3:91:56:80:2f:8c:04:2f:ad:57:ef:\n d2:51:19:f4:b4:ef:32:9c:ac:3a:7c:0d:b8:39:db:b1:e3:30:\n 73:1a\n> +1999-03-02 09:44:33 SAN +1999-03-02 09:44:33 OCU +1999-03-02 09:44:33 CRU +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLSv1:AES256-SHA:256 DN="/CN=server2.example.com" S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER R=abc T=local_delivery 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed diff --git a/test/log/5750 b/test/log/5750 new file mode 100644 index 000000000..8c98b5b95 --- /dev/null +++ b/test/log/5750 @@ -0,0 +1,46 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (certificate verification failed): certificate invalid +1999-03-02 09:44:33 10HmaX-0005vi-00 deferral bad +1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented +1999-03-02 09:44:33 10HmaX-0005vi-00 Peer cert: +1999-03-02 09:44:33 10HmaX-0005vi-00 ver <3> +1999-03-02 09:44:33 10HmaX-0005vi-00 SN +1999-03-02 09:44:33 10HmaX-0005vi-00 IN +1999-03-02 09:44:33 10HmaX-0005vi-00 NB +1999-03-02 09:44:33 10HmaX-0005vi-00 NA +1999-03-02 09:44:33 10HmaX-0005vi-00 SA +1999-03-02 09:44:33 10HmaX-0005vi-00 SG <56 3a a4 3c cb eb b8 27 c2 90 08 74 13 88 dc 48 c6 b5 2c e5 26 be 5b 91 d4 67 e7 3c 49 12 d7 47 30 df 98 db 58 ed 18 a8 7d 4b db 97 48 f5 5c 7f 70 b9 37 63 33 f1 24 62 72 92 60 f5 6e da b6 bc 73 c8 c2 dc d6 95 9a bd 16 16 a2 ef 0a f1 d7 41 68 f6 ad 98 5a d0 ff d9 1b 51 9f 59 ce 2f 3d 84 d0 ee e8 2b eb 9b 32 1a 0e 02 3e cc 30 89 44 09 2a 75 81 46 a7 b6 ed 7d 41 eb 5a 63 fa 9c 58 ef> +1999-03-02 09:44:33 10HmaX-0005vi-00 SAN +1999-03-02 09:44:33 10HmaX-0005vi-00 OCU +1999-03-02 09:44:33 10HmaX-0005vi-00 CRU +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls) +1999-03-02 09:44:33 10HmaX-0005vi-00 => bad@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 delivery bad +1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented +1999-03-02 09:44:33 10HmaX-0005vi-00 No Peer cert +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed +1999-03-02 09:44:33 10HmaY-0005vi-00 => good@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server1.example.com" C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaY-0005vi-00 delivery good +1999-03-02 09:44:33 10HmaY-0005vi-00 Our cert SN: CN=server2.example.com +1999-03-02 09:44:33 10HmaY-0005vi-00 Peer cert: +1999-03-02 09:44:33 10HmaY-0005vi-00 ver <3> +1999-03-02 09:44:33 10HmaY-0005vi-00 SN +1999-03-02 09:44:33 10HmaY-0005vi-00 IN +1999-03-02 09:44:33 10HmaY-0005vi-00 NB +1999-03-02 09:44:33 10HmaY-0005vi-00 NA +1999-03-02 09:44:33 10HmaY-0005vi-00 SA +1999-03-02 09:44:33 10HmaY-0005vi-00 SG <56 3a a4 3c cb eb b8 27 c2 90 08 74 13 88 dc 48 c6 b5 2c e5 26 be 5b 91 d4 67 e7 3c 49 12 d7 47 30 df 98 db 58 ed 18 a8 7d 4b db 97 48 f5 5c 7f 70 b9 37 63 33 f1 24 62 72 92 60 f5 6e da b6 bc 73 c8 c2 dc d6 95 9a bd 16 16 a2 ef 0a f1 d7 41 68 f6 ad 98 5a d0 ff d9 1b 51 9f 59 ce 2f 3d 84 d0 ee e8 2b eb 9b 32 1a 0e 02 3e cc 30 89 44 09 2a 75 81 46 a7 b6 ed 7d 41 eb 5a 63 fa 9c 58 ef> +1999-03-02 09:44:33 10HmaY-0005vi-00 SAN +1999-03-02 09:44:33 10HmaY-0005vi-00 OCU +1999-03-02 09:44:33 10HmaY-0005vi-00 CRU +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 TLS error on connection from localhost [127.0.0.1] (recv): A TLS fatal alert has been received.: Certificate is bad +1999-03-02 09:44:33 TLS error on connection from localhost [127.0.0.1] (send): The specified session has been invalidated for some reason. +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server2.example.com" S=sss id=E10HmaY-0005vi-00@myhost.test.ex diff --git a/test/log/5760 b/test/log/5760 new file mode 100644 index 000000000..0b74e243d --- /dev/null +++ b/test/log/5760 @@ -0,0 +1,47 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaX-0005vi-00 SSL verify error: depth=2 error=self signed certificate in certificate chain cert=/O=example.com/CN=clica CA +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (SSL_connect): error: <> +1999-03-02 09:44:33 10HmaX-0005vi-00 deferral bad +1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented +1999-03-02 09:44:33 10HmaX-0005vi-00 Peer cert: +1999-03-02 09:44:33 10HmaX-0005vi-00 ver <2> +1999-03-02 09:44:33 10HmaX-0005vi-00 SN +1999-03-02 09:44:33 10HmaX-0005vi-00 IN +1999-03-02 09:44:33 10HmaX-0005vi-00 NB +1999-03-02 09:44:33 10HmaX-0005vi-00 NA +1999-03-02 09:44:33 10HmaX-0005vi-00 SA +1999-03-02 09:44:33 10HmaX-0005vi-00 SG < Signature Algorithm: sha1WithRSAEncryption\n 89:fd:fb:cb:b2:42:d6:aa:f2:c0:44:a2:14:e5:ab:22:50:41:\n e6:64:e7:1c:5a:20:b6:0f:fe:b0:88:c5:cf:b3:e5:f8:0e:87:\n eb:ac:07:d6:9d:6a:20:f6:dd:13:ee:b8:3f:cf:d9:cd:d4:a8:\n 72:50:5a:a2:14:4e:ee:3a:78:e2:a7:f4:ae:d7:ee:77:48:1f:\n 75:a7:68:2f:ee:e2:7c:ac:2f:e4:88:02:e8:3b:db:f9:35:04:\n 05:46:35:0b:f2:35:03:21:b6:1e:82:7d:94:e0:63:4b:60:71:\n 2d:19:45:21:f2:85:b4:c3:d0:77:a2:24:32:36:f3:50:68:38:\n 98:e6\n> +1999-03-02 09:44:33 10HmaX-0005vi-00 (no SAN) +1999-03-02 09:44:33 10HmaX-0005vi-00 (no OCU) +1999-03-02 09:44:33 10HmaX-0005vi-00 (no CRU) +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls) +1999-03-02 09:44:33 10HmaX-0005vi-00 => bad@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 delivery bad +1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented +1999-03-02 09:44:33 10HmaX-0005vi-00 No Peer cert +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed +1999-03-02 09:44:33 10HmaY-0005vi-00 => good@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLSv1:AES256-SHA:256 DN="/CN=server1.example.com" C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaY-0005vi-00 delivery good +1999-03-02 09:44:33 10HmaY-0005vi-00 Our cert SN: CN=server2.example.com +1999-03-02 09:44:33 10HmaY-0005vi-00 Peer cert: +1999-03-02 09:44:33 10HmaY-0005vi-00 ver <2> +1999-03-02 09:44:33 10HmaY-0005vi-00 SN +1999-03-02 09:44:33 10HmaY-0005vi-00 IN +1999-03-02 09:44:33 10HmaY-0005vi-00 NB +1999-03-02 09:44:33 10HmaY-0005vi-00 NA +1999-03-02 09:44:33 10HmaY-0005vi-00 SA +1999-03-02 09:44:33 10HmaY-0005vi-00 SG < Signature Algorithm: sha1WithRSAEncryption\n 56:3a:a4:3c:cb:eb:b8:27:c2:90:08:74:13:88:dc:48:c6:b5:\n 2c:e5:26:be:5b:91:d4:67:e7:3c:49:12:d7:47:30:df:98:db:\n 58:ed:18:a8:7d:4b:db:97:48:f5:5c:7f:70:b9:37:63:33:f1:\n 24:62:72:92:60:f5:6e:da:b6:bc:73:c8:c2:dc:d6:95:9a:bd:\n 16:16:a2:ef:0a:f1:d7:41:68:f6:ad:98:5a:d0:ff:d9:1b:51:\n 9f:59:ce:2f:3d:84:d0:ee:e8:2b:eb:9b:32:1a:0e:02:3e:cc:\n 30:89:44:09:2a:75:81:46:a7:b6:ed:7d:41:eb:5a:63:fa:9c:\n 58:ef\n> +1999-03-02 09:44:33 10HmaY-0005vi-00 SAN +1999-03-02 09:44:33 10HmaY-0005vi-00 OCU +1999-03-02 09:44:33 10HmaY-0005vi-00 CRU +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] (SSL_accept): error: <> +1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?) +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 DN="/CN=server2.example.com" S=sss id=E10HmaY-0005vi-00@myhost.test.ex diff --git a/test/mail/2002.CALLER b/test/mail/2002.CALLER index a4e0dd526..23b5f61a5 100644 --- a/test/mail/2002.CALLER +++ b/test/mail/2002.CALLER @@ -30,7 +30,7 @@ Received: from [ip4.ip4.ip4.ip4] id 10HmaZ-0005vi-00 for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 tls-certificate-verified: 1 -TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock +TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=CN=server2.example.com This is a test encrypted message from a verified host. diff --git a/test/mail/2102.CALLER b/test/mail/2102.CALLER index e4be6a342..42c189f78 100644 --- a/test/mail/2102.CALLER +++ b/test/mail/2102.CALLER @@ -30,7 +30,7 @@ Received: from [ip4.ip4.ip4.ip4] id 10HmaZ-0005vi-00 for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 tls-certificate-verified: 1 -TLS: cipher=TLSv1:AES256-SHA:256 peerdn=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +TLS: cipher=TLSv1:AES256-SHA:256 peerdn=/CN=server2.example.com This is a test encrypted message from a verified host. diff --git a/test/scripts/2000-GnuTLS/2002 b/test/scripts/2000-GnuTLS/2002 index 06a7b31d0..49f841e56 100644 --- a/test/scripts/2000-GnuTLS/2002 +++ b/test/scripts/2000-GnuTLS/2002 @@ -1,4 +1,4 @@ -# TLS server: general +# TLS server: general ops and certificate extractions gnutls exim -DSERVER=server -bd -oX PORT_D **** @@ -60,7 +60,7 @@ ehlo rhu.barb starttls ??? 220 **** -client-gnutls HOSTIPV4 PORT_D DIR/aux-fixed/cert2 DIR/aux-fixed/cert2 +client-gnutls HOSTIPV4 PORT_D DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key ??? 220 ehlo rhu.barb ??? 250- diff --git a/test/scripts/2100-OpenSSL/2102 b/test/scripts/2100-OpenSSL/2102 index 2e7dca0a6..cbb9ce393 100644 --- a/test/scripts/2100-OpenSSL/2102 +++ b/test/scripts/2100-OpenSSL/2102 @@ -1,4 +1,4 @@ -# TLS server: general +# TLS server: general ops and certificate extractions exim -DSERVER=server -bd -oX PORT_D **** client-ssl 127.0.0.1 PORT_D @@ -59,7 +59,7 @@ ehlo rhu.barb starttls ??? 220 **** -client-ssl HOSTIPV4 PORT_D DIR/aux-fixed/cert2 DIR/aux-fixed/cert2 +client-ssl HOSTIPV4 PORT_D DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key ??? 220 ehlo rhu.barb ??? 250- diff --git a/test/scripts/5750-GnuTLS-TPDA/5750 b/test/scripts/5750-GnuTLS-TPDA/5750 new file mode 100644 index 000000000..903c79525 --- /dev/null +++ b/test/scripts/5750-GnuTLS-TPDA/5750 @@ -0,0 +1,13 @@ +# TLS client: GnuTLS $tls_out_peercert +exim -DSERVER=server -bd -oX PORT_D +**** +exim bad@test.ex +Testing +**** +exim good@test.ex +Testing +**** +exim -qf +**** +killdaemon +no_msglog_check diff --git a/test/scripts/5750-GnuTLS-TPDA/REQUIRES b/test/scripts/5750-GnuTLS-TPDA/REQUIRES new file mode 100644 index 000000000..af1eb46f7 --- /dev/null +++ b/test/scripts/5750-GnuTLS-TPDA/REQUIRES @@ -0,0 +1,2 @@ +support Experimental_TPDA +support GnuTLS diff --git a/test/scripts/5760-OpenSSL-TPDA/5760 b/test/scripts/5760-OpenSSL-TPDA/5760 new file mode 100644 index 000000000..8fa8bd04b --- /dev/null +++ b/test/scripts/5760-OpenSSL-TPDA/5760 @@ -0,0 +1,13 @@ +# TLS client: OpenSSL certificates and extractions +exim -DSERVER=server -bd -oX PORT_D +**** +exim bad@test.ex +Testing +**** +exim good@test.ex +Testing +**** +exim -qf +**** +killdaemon +no_msglog_check diff --git a/test/scripts/5760-OpenSSL-TPDA/REQUIRES b/test/scripts/5760-OpenSSL-TPDA/REQUIRES new file mode 100644 index 000000000..5b4892059 --- /dev/null +++ b/test/scripts/5760-OpenSSL-TPDA/REQUIRES @@ -0,0 +1,2 @@ +support Experimental_TPDA +support OpenSSL diff --git a/test/stdout/2002 b/test/stdout/2002 index a248be7c0..ec3c1f954 100644 --- a/test/stdout/2002 +++ b/test/stdout/2002 @@ -97,8 +97,8 @@ Attempting to start TLS Failed to start TLS End of script Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected -Certificate file = TESTSUITE/aux-fixed/cert2 -Key file = TESTSUITE/aux-fixed/cert2 +Certificate file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem +Key file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key ??? 220 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 >>> ehlo rhu.barb diff --git a/test/stdout/2102 b/test/stdout/2102 index 23c39cdf4..77ae109b2 100644 --- a/test/stdout/2102 +++ b/test/stdout/2102 @@ -145,8 +145,8 @@ pppp:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s Failed to start TLS End of script Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected -Certificate file = TESTSUITE/aux-fixed/cert2 -Key file = TESTSUITE/aux-fixed/cert2 +Certificate file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem +Key file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key ??? 220 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 >>> ehlo rhu.barb