From 59b87190a41a0ac34aee74edfff9184785a73485 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 30 Dec 2015 20:39:45 +0000 Subject: Support certificates in base64 expansion operator. Bug 1762 --- test/confs/2002 | 1 + test/confs/2102 | 1 + 2 files changed, 2 insertions(+) (limited to 'test/confs') diff --git a/test/confs/2002 b/test/confs/2002 index 7299122e8..76e6f1eec 100644 --- a/test/confs/2002 +++ b/test/confs/2002 @@ -64,6 +64,7 @@ check_recipient: logwrite = md5 fingerprint ${md5:$tls_in_peercert} logwrite = sha1 fingerprint ${sha1:$tls_in_peercert} logwrite = sha256 fingerprint ${sha256:$tls_in_peercert} + logwrite = der_b64 ${base64:$tls_in_peercert} # ----- Routers ----- diff --git a/test/confs/2102 b/test/confs/2102 index 804a846bf..1de92e962 100644 --- a/test/confs/2102 +++ b/test/confs/2102 @@ -68,6 +68,7 @@ check_recipient: logwrite = md5 fingerprint ${md5:$tls_in_peercert} logwrite = sha1 fingerprint ${sha1:$tls_in_peercert} logwrite = sha256 fingerprint ${sha256:$tls_in_peercert} + logwrite = der_b64 ${base64:$tls_in_peercert} # ----- Routers ----- -- cgit v1.2.3 From 69a70afa8b22ee4ee72ccf583db2efd249e36721 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 5 Jan 2016 14:54:02 +0000 Subject: DKIM: fix base64 decode to ignore whitespace; needed for private-key input from file. Use this for general-purpose b64decode also. Testsuite: DKIM signing testcase --- src/src/base64.c | 17 ++++++-- src/src/pdkim/pdkim-rsa.c | 20 ++++++++- src/src/pdkim/pdkim.c | 8 +++- test/confs/4503 | 47 ++++++++++++++++++++++ test/dnszones-src/db.test.ex | 2 +- test/log/4503 | 9 +++++ test/scripts/4500-Domain-Keys-Identified-Mail/4503 | 10 +++++ 7 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 test/confs/4503 create mode 100644 test/log/4503 create mode 100644 test/scripts/4500-Domain-Keys-Identified-Mail/4503 (limited to 'test/confs') diff --git a/src/src/base64.c b/src/src/base64.c index f4c4f233b..61b600f6c 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -134,6 +134,7 @@ Arguments: Returns: the number of bytes in the result, or -1 if the input was malformed +Whitespace in the input is ignored. A zero is added on to the end to make it easy in cases where the result is to be interpreted as text. This is not included in the count. */ @@ -161,21 +162,29 @@ quantum this may decode to 1, 2, or 3 output bytes. */ while ((x = *code++) != 0) { + if (isspace(x)) continue; + if (x > 127 || (x = dec64table[x]) == 255) return -1; - if ((y = *code++) == 0 || (y = dec64table[y]) == 255) + + while (isspace(y = *code++)) ; + if (y == 0 || (y = dec64table[y]) == 255) return -1; *result++ = (x << 2) | (y >> 4); - if ((x = *code++) == '=') + while (isspace(x = *code++)) ; + if (x == '=') { - if (*code++ != '=' || *code != 0) return -1; + while (isspace(x = *code++)) ; + if (x != '=' || *code != 0) return -1; } else { if (x > 127 || (x = dec64table[x]) == 255) return -1; *result++ = (y << 4) | (x >> 2); - if ((y = (*code++)) == '=') + + while (isspace(y = *code++)) ; + if (y == '=') { if (*code != 0) return -1; } diff --git a/src/src/pdkim/pdkim-rsa.c b/src/src/pdkim/pdkim-rsa.c index 87cbac130..7f98fb008 100644 --- a/src/src/pdkim/pdkim-rsa.c +++ b/src/src/pdkim/pdkim-rsa.c @@ -82,17 +82,25 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, "-----END RSA PRIVATE KEY-----" ); if( s2 == NULL || s2 <= s1 ) +{ +debug_printf("rsa_parse_key: err 1\n"); return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); +} s1 += 31; if( *s1 == '\r' ) s1++; if( *s1 == '\n' ) s1++; - else return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + else +{ +debug_printf("rsa_parse_key: err 2\n"); + return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); +} enc = 0; if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) { +debug_printf("rsa_parse_key: err 3\n"); return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); } @@ -104,14 +112,18 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, s1 = string_copyn(s1, s2-s1); /* need nul-terminated string */ if ((len = b64decode(s1, &buf)) < 0) +{ +debug_printf("rsa_parse_key: err 4\n"); return POLARSSL_ERR_BASE64_INVALID_CHARACTER | POLARSSL_ERR_X509_KEY_INVALID_PEM; +} } buflen = len; if( enc != 0 ) { +debug_printf("rsa_parse_key: err 5\n"); return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); } } @@ -139,6 +151,7 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 6\n"); return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); } @@ -147,12 +160,14 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 7\n"); return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); } if( rsa->ver != 0 ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 8\n"); return( ret | POLARSSL_ERR_X509_KEY_INVALID_VERSION ); } @@ -166,6 +181,7 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 9\n"); return( ret | POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); } @@ -174,6 +190,7 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, if( p != end ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 10\n"); return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); } @@ -181,6 +198,7 @@ int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) { rsa_free( rsa ); +debug_printf("rsa_parse_key: err 11\n"); return( ret ); } diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index bd05c51b5..f93bda087 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1759,13 +1759,17 @@ while (sig) if (ctx->mode == PDKIM_MODE_SIGN) { rsa_context rsa; +int perr; rsa_init(&rsa, RSA_PKCS_V15, 0); /* Perform private key operation */ - if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey, - strlen(sig->rsa_privkey), NULL, 0) != 0) + if ((perr = rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey, + strlen(sig->rsa_privkey), NULL, 0)) != 0) +{ +debug_printf("rsa_parse_key: perr 0x%x\n", perr); return PDKIM_ERR_RSA_PRIVKEY; +} sig->sigdata_len = mpi_size(&(rsa.N)); sig->sigdata = store_get(sig->sigdata_len); diff --git a/test/confs/4503 b/test/confs/4503 new file mode 100644 index 000000000..e9f2d5d47 --- /dev/null +++ b/test/confs/4503 @@ -0,0 +1,47 @@ +# Exim test configuration 4503 + +SERVER= + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/SERVER%slog +gecos_pattern = "" +gecos_name = CALLER_NAME +tls_advertise_hosts = + +# ----- Main settings ----- + +log_selector = +outgoing_port +acl_smtp_rcpt = accept +acl_smtp_dkim = accept logwrite = signer: $dkim_cur_signer bits: $dkim_key_length + +# ----- Routers + +begin routers + +server_dump: + driver = redirect + condition = ${if eq {SERVER}{server}{yes}{no}} + data = :blackhole: + +client: + driver = accept + transport = send_to_server + +# ----- Transports + +begin transports + +send_to_server: + driver = smtp + allow_localhost + hosts = HOSTIPV4 + port = PORT_D + + dkim_domain = test.ex + dkim_selector = sel + dkim_private_key = DIR/aux-fixed/dkim/dkim.private + +# End diff --git a/test/dnszones-src/db.test.ex b/test/dnszones-src/db.test.ex index 859626106..cde5b4321 100644 --- a/test/dnszones-src/db.test.ex +++ b/test/dnszones-src/db.test.ex @@ -472,7 +472,7 @@ DELAY=1500 delay1500 A HOSTIPV4 ; ------- DKIM --------- -; public key, base64 - matches private key in aux-fixed/dkim/dkim/private +; public key, base64 - matches private key in aux-fixed/dkim/dkim.private ; openssl genrsa -out aux-fixed/dkim/dkim.private 1024 ; openssl rsa -in aux-fixed/dkim/dkim.private -out /dev/stdout -pubout -outform PEM ; diff --git a/test/log/4503 b/test/log/4503 new file mode 100644 index 000000000..056b52946 --- /dev/null +++ b/test/log/4503 @@ -0,0 +1,9 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss + +******** 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 10HmaY-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] +1999-03-02 09:44:33 10HmaY-0005vi-00 signer: test.ex bits: 1024 +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=server_dump +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/scripts/4500-Domain-Keys-Identified-Mail/4503 b/test/scripts/4500-Domain-Keys-Identified-Mail/4503 new file mode 100644 index 000000000..7ca338275 --- /dev/null +++ b/test/scripts/4500-Domain-Keys-Identified-Mail/4503 @@ -0,0 +1,10 @@ +# DKIM signing +# +exim -bd -DSERVER=server -oX PORT_D +**** +exim a@test.ex +content +**** +millisleep 200 +killdaemon +no_msglog_check -- cgit v1.2.3 From 71fe7747abbcdac393e93bbc0d84d94149f54aa0 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 11 Jan 2016 14:25:02 +0000 Subject: Testsuite: split out conf for -bP test and lose dependency on Proxy/Socks --- test/confs/0572 | 48 +++++++++++++++++++++++++++++++++++++++++++++++- test/stdout/0572 | 2 +- 2 files changed, 48 insertions(+), 2 deletions(-) mode change 120000 => 100644 test/confs/0572 (limited to 'test/confs') diff --git a/test/confs/0572 b/test/confs/0572 deleted file mode 120000 index 4af051ca9..000000000 --- a/test/confs/0572 +++ /dev/null @@ -1 +0,0 @@ -4020 \ No newline at end of file diff --git a/test/confs/0572 b/test/confs/0572 new file mode 100644 index 000000000..2cce49c38 --- /dev/null +++ b/test/confs/0572 @@ -0,0 +1,47 @@ +# Exim test configuration 0572 + +OPT = + +exim_path = EXIM_PATH +hide host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/%slog +gecos_pattern = "" +gecos_name = CALLER_NAME +tls_advertise_hosts = + +# ----- Main settings ----- + +log_selector = +outgoing_port + +domainlist local_domains = test.ex : *.test.ex +acl_smtp_rcpt = accept + + +# ----- Routers ----- + +begin routers + +my_main_router: + driver = manualroute + route_list = * 127.0.0.1 + self = send + transport = my_smtp + debug_print = router_name <$router_name> + no_more + + +# ----- Transports ----- + +begin transports + +my_smtp: + driver = smtp + interface = HOSTIPV4 + port = PORT_S + hide socks_proxy = 127.0.0.1 port=PORT_D OPT + debug_print = transport_name <$transport_name> + + +# End diff --git a/test/stdout/0572 b/test/stdout/0572 index 87a02d3ed..bd537bc44 100644 --- a/test/stdout/0572 +++ b/test/stdout/0572 @@ -107,7 +107,7 @@ log_file_path = TESTSUITE/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME tls_advertise_hosts = -log_selector = +proxy +outgoing_port +log_selector = +outgoing_port domainlist local_domains = test.ex : *.test.ex acl_smtp_rcpt = accept -- cgit v1.2.3 From 4f6ae5c314e5c3e462313f3b53c917f36b131bf4 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 16 Jan 2016 22:17:33 +0000 Subject: VRFY: Permit an ACL to override the default 252 response, to support verify-by-ACL instead of the more usual verify-by-routers. Bug 1769 --- doc/doc-docbook/spec.xfpt | 11 ++- doc/doc-txt/ChangeLog | 3 + src/src/acl.c | 6 +- src/src/functions.h | 2 +- src/src/receive.c | 10 +-- src/src/smtp_in.c | 161 +++++++++++++++++++++---------------------- test/confs/0041 | 5 ++ test/scripts/0000-Basic/0041 | 1 + test/stderr/0041 | 15 +++- test/stdout/0041 | 1 + 10 files changed, 122 insertions(+), 93 deletions(-) (limited to 'test/confs') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index aa1e67712..cb913d6f1 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -34046,13 +34046,20 @@ specific badly-behaved hosts that you have to live with. When Exim receives a VRFY or EXPN command on a TCP/IP connection, it runs the ACL specified by &%acl_smtp_vrfy%& or &%acl_smtp_expn%& (as appropriate) in order to decide whether the command should be accepted or not. -If no ACL is defined, the command is rejected. +.new .cindex "VRFY" "processing" +When no ACL is defined for VRFY, or if it rejects without +setting an explicit response code, the command is accepted +(with a 252 SMTP response code) +in order to support awkward clients that do a VRFY before every RCPT. +.wen When VRFY is accepted, it runs exactly the same code as when Exim is -called with the &%-bv%& option. +called with the &%-bv%& option, and returns 250/451/550 +SMTP response codes. .cindex "EXPN" "processing" +If no ACL for EXPN is defined, the command is rejected. When EXPN is accepted, a single-level expansion of the address is done. EXPN is treated as an &"address test"& (similar to the &%-bt%& option) rather than a verification (the &%-bv%& option). If an unqualified local part is given diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 28fe75447..9f275a9db 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -153,6 +153,9 @@ JH/36 Fix a longstanding bug in memory use by the ${run } expansion: A fresh concluded, but leaving the global pointer active for it. Possibly involved in Bug 1778. +JH/37 Bug 1769: Permit a VRFY ACL to override the default 252 response, + and to use the domains and local_parts ACL conditions. + Exim version 4.86 ----------------- diff --git a/src/src/acl.c b/src/src/acl.c index 1456cc724..684b93bbb 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -482,6 +482,7 @@ static unsigned int cond_forbids[] = { (unsigned int) ~((1< 4) { esc = code + 4; @@ -2626,23 +2626,24 @@ Arguments: codelen length of smtp code; if > 4 there's an ESC msg message text log_msg optional log message, to be adjusted with the new SMTP code + check_valid if true, verify the response code Returns: nothing */ void -smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg) +smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg, + BOOL check_valid) { int n; int ovector[3]; -if (msg == NULL || *msg == NULL) return; +if (!msg || !*msg) return; -n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0, - PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)); -if (n < 0) return; +if ((n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0, + PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int))) < 0) return; -if ((*msg)[0] != (*code)[0]) +if (check_valid && (*msg)[0] != (*code)[0]) { log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with " "incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg); @@ -2677,18 +2678,19 @@ defaults disabled in Exim. However, discussion in connection with RFC 821bis (aka RFC 2821) has concluded that the response should be 252 in the disabled state, because there are broken clients that try VRFY before RCPT. A 5xx response should be given only when the address is positively known to be -undeliverable. Sigh. Also, for ETRN, 458 is given on refusal, and for AUTH, -503. +undeliverable. Sigh. We return 252 if there is no VRFY ACL or it provides +no explicit code, but if there is one we let it know best. +Also, for ETRN, 458 is given on refusal, and for AUTH, 503. From Exim 4.63, it is possible to override the response code details by providing a suitable response code string at the start of the message provided in user_msg. The code's first digit is checked for validity. Arguments: - where where the ACL was called from - rc the failure code - user_msg a message that can be included in an SMTP response - log_msg a message for logging + where where the ACL was called from + rc the failure code + user_msg a message that can be included in an SMTP response + log_msg a message for logging Returns: 0 in most cases 2 if the failure code was FAIL_DROP, in which case the @@ -2721,8 +2723,9 @@ if (drop) rc = FAIL; /* Set the default SMTP code, and allow a user message to change it. */ -smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where]; -smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg); +smtp_code = rc == FAIL ? acl_wherecodes[where] : US"451"; +smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg, + where != ACL_WHERE_VRFY); /* We used to have sender_address here; however, there was a bug that was not updating sender_address after a rewrite during a verify. When this bug was @@ -3096,7 +3099,7 @@ static void smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; -smtp_message_code(&code, &len, &user_msg, NULL); +smtp_message_code(&code, &len, &user_msg, NULL, TRUE); smtp_respond(code, len, TRUE, user_msg); } @@ -3305,14 +3308,13 @@ while (done <= 0) ) { cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; - if (acl_smtp_auth) + if ( acl_smtp_auth + && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, + &user_msg, &log_msg)) != OK + ) { - rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); - if (rc != OK) - { - done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); - continue; - } + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + continue; } for (au = auths; au; au = au->next) @@ -3371,14 +3373,13 @@ while (done <= 0) /* Check the ACL */ - if (acl_smtp_auth) + if ( acl_smtp_auth + && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, + &user_msg, &log_msg)) != OK + ) { - rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); - if (rc != OK) - { - done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); - break; - } + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + break; } /* Find the name of the requested authentication mechanism. */ @@ -3550,10 +3551,9 @@ while (done <= 0) /* Apply an ACL check if one is defined; afterwards, recheck synchronization in case the client started sending in a delay. */ - if (acl_smtp_helo != NULL) - { - rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg); - if (rc != OK) + if (acl_smtp_helo) + if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, + &user_msg, &log_msg)) != OK) { done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg); sender_helo_name = NULL; @@ -3561,7 +3561,6 @@ while (done <= 0) break; } else if (!check_sync()) goto SYNC_FAILURE; - } /* Generate an OK reply. The default string includes the ident if present, and also the IP address if present. Reflecting back the ident is intended @@ -3609,7 +3608,7 @@ while (done <= 0) { char *ss; int codelen = 4; - smtp_message_code(&smtp_code, &codelen, &user_msg, NULL); + smtp_message_code(&smtp_code, &codelen, &user_msg, NULL, TRUE); s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg); if ((ss = strpbrk(CS s, "\r\n")) != NULL) { @@ -4581,51 +4580,49 @@ while (done <= 0) case VRFY_CMD: - HAD(SCH_VRFY); - rc = acl_check(ACL_WHERE_VRFY, NULL, acl_smtp_vrfy, &user_msg, &log_msg); - if (rc != OK) - done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg); - else { - uschar *address; - uschar *s = NULL; + uschar * address; - /* rfc821_domains = TRUE; << no longer needed */ - address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end, - &recipient_domain, FALSE); - /* rfc821_domains = FALSE; << no longer needed */ + HAD(SCH_VRFY); - if (address == NULL) - s = string_sprintf("501 %s", errmess); + if(!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end, + &recipient_domain, FALSE))) + smtp_printf("501 %s\r\n", errmess); + + else if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy, + &user_msg, &log_msg)) != OK) + done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg); else - { - address_item *addr = deliver_make_addr(address, FALSE); - switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1, - -1, -1, NULL, NULL, NULL)) - { - case OK: - s = string_sprintf("250 <%s> is deliverable", address); - break; + { + uschar *s = NULL; - case DEFER: - s = (addr->user_message != NULL)? - string_sprintf("451 <%s> %s", address, addr->user_message) : - string_sprintf("451 Cannot resolve <%s> at this time", address); - break; + address_item *addr = deliver_make_addr(address, FALSE); + switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1, + -1, -1, NULL, NULL, NULL)) + { + case OK: + s = string_sprintf("250 <%s> is deliverable", address); + break; - case FAIL: - s = (addr->user_message != NULL)? - string_sprintf("550 <%s> %s", address, addr->user_message) : - string_sprintf("550 <%s> is not deliverable", address); - log_write(0, LOG_MAIN, "VRFY failed for %s %s", - smtp_cmd_argument, host_and_ident(TRUE)); - break; - } - } + case DEFER: + s = (addr->user_message != NULL)? + string_sprintf("451 <%s> %s", address, addr->user_message) : + string_sprintf("451 Cannot resolve <%s> at this time", address); + break; - smtp_printf("%s\r\n", s); + case FAIL: + s = (addr->user_message != NULL)? + string_sprintf("550 <%s> %s", address, addr->user_message) : + string_sprintf("550 <%s> is not deliverable", address); + log_write(0, LOG_MAIN, "VRFY failed for %s %s", + smtp_cmd_argument, host_and_ident(TRUE)); + break; + } + + smtp_printf("%s\r\n", s); + } + break; } - break; case EXPN_CMD: @@ -4659,15 +4656,13 @@ while (done <= 0) /* Apply an ACL check if one is defined */ - if (acl_smtp_starttls != NULL) + if ( acl_smtp_starttls + && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, + &user_msg, &log_msg)) != OK + ) { - rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, &user_msg, - &log_msg); - if (rc != OK) - { - done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg); - break; - } + done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg); + break; } /* RFC 2487 is not clear on when this command may be sent, though it @@ -4910,8 +4905,8 @@ while (done <= 0) log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument, host_and_ident(FALSE)); - rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, &user_msg, &log_msg); - if (rc != OK) + if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, + &user_msg, &log_msg)) != OK) { done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg); break; diff --git a/test/confs/0041 b/test/confs/0041 index 51cba04ac..ec1067584 100644 --- a/test/confs/0041 +++ b/test/confs/0041 @@ -12,6 +12,7 @@ tls_advertise_hosts = domainlist local_domains = test.ex +acl_smtp_vrfy = check_vrfy acl_smtp_expn = check_expn qualify_domain = test.ex no_write_rejectlog @@ -21,6 +22,10 @@ no_write_rejectlog begin acl +check_vrfy: + deny local_parts = hardfail + message = 599 custom reject + check_expn: accept hosts = 2.2.2.2 diff --git a/test/scripts/0000-Basic/0041 b/test/scripts/0000-Basic/0041 index 5601d6506..3495375cb 100644 --- a/test/scripts/0000-Basic/0041 +++ b/test/scripts/0000-Basic/0041 @@ -1,6 +1,7 @@ # VRFY & EXPN blocking exim -bh 1.1.1.1 vrfy userx@test.ex +vrfy hardfail@test.ex expn postmaster quit **** diff --git a/test/stderr/0041 b/test/stderr/0041 index 4aacae883..b00305212 100644 --- a/test/stderr/0041 +++ b/test/stderr/0041 @@ -7,8 +7,21 @@ >>> host in helo_try_verify_hosts? no (option unset) >>> host in helo_accept_junk_hosts? no (option unset) >>> host in smtp_accept_max_nonmail_hosts? yes (matched "*") ->>> ACL is NULL: implicit DENY +>>> using ACL "check_vrfy" +>>> processing "deny" +>>> check local_parts = hardfail +>>> userx in "hardfail"? no (end of list) +>>> deny: condition test failed in ACL "check_vrfy" +>>> end of ACL "check_vrfy": implicit DENY LOG: H=[1.1.1.1] rejected VRFY userx@test.ex +>>> using ACL "check_vrfy" +>>> processing "deny" +>>> check local_parts = hardfail +>>> hardfail in "hardfail"? yes (matched "hardfail") +>>> message: 599 custom reject +>>> deny: condition test succeeded in ACL "check_vrfy" +>>> end of ACL "check_vrfy": DENY +LOG: H=[1.1.1.1] rejected VRFY hardfail@test.ex: 599 custom reject >>> using ACL "check_expn" >>> processing "accept" >>> check hosts = 2.2.2.2 diff --git a/test/stdout/0041 b/test/stdout/0041 index d43b4501a..b88c93ac7 100644 --- a/test/stdout/0041 +++ b/test/stdout/0041 @@ -5,6 +5,7 @@ 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 252 Administrative prohibition +599 custom reject 550 Administrative prohibition 221 the.local.host.name closing connection -- cgit v1.2.3 From 62b7cd086e8f69c7bb1334edd2e586ed6240b134 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 17 Jan 2016 21:14:31 +0000 Subject: Restrict line lengths in bounces. Bug 1760 --- doc/doc-docbook/spec.xfpt | 19 ++++ doc/doc-txt/NewStuff | 4 + src/src/deliver.c | 24 +++-- src/src/globals.c | 1 + src/src/globals.h | 1 + src/src/moan.c | 246 +++++++++++++++++++++++-------------------- src/src/readconf.c | 1 + test/confs/0001 | 1 + test/confs/0573 | 37 +++++++ test/scripts/0000-Basic/0573 | 18 ++++ 10 files changed, 227 insertions(+), 125 deletions(-) create mode 100644 test/confs/0573 create mode 100644 test/scripts/0000-Basic/0573 (limited to 'test/confs') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index cb913d6f1..cd06de8d9 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -13786,6 +13786,7 @@ See also the &'Policy controls'& section above. .row &%bounce_message_file%& "content of bounce" .row &%bounce_message_text%& "content of bounce" .row &%bounce_return_body%& "include body if returning message" +.row &%bounce_return_linesize_limit%& "limit on returned message line length" .row &%bounce_return_message%& "include original message in bounce" .row &%bounce_return_size_limit%& "limit on returned message" .row &%bounce_sender_authentication%& "send authenticated sender with bounce" @@ -14094,6 +14095,24 @@ error that is detected during reception, only those header lines preceding the point at which the error was detected are returned. .cindex "bounce message" "including original" +.option bounce_return_linesize_limit main integer 998 +.cindex "size" "of bounce lines, limit" +.cindex "bounce message" "line length limit" +.cindex "limit" "bounce message line length" +This option sets a limit in bytes on the line length of messages +that are returned to senders due to delivery problems, +when &%bounce_return_message%& is true. +The default value corresponds to RFC limits. +If the message being returned has lines longer than this value it is +treated as if the &%bounce_return_size_limit%& (below) restriction was exceeded. + +The option also applies to bounces returned when an error is detected +during reception of a messsage. +In this case lines from the original are truncated. + +The option does not apply to messages generated by an &(autoreply)& transport. + + .option bounce_return_message main boolean true If this option is set false, none of the original message is included in bounce messages generated by Exim. See also &%bounce_return_size_limit%& and diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index e4d1f0607..01bd0111e 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -31,6 +31,10 @@ Version 4.87 7. New base64d and base64 expansion items (the existing str2b64 being a synonym of the latter). Add support in base64 for certificates. + 8. New main configuration option "bounce_return_linesize_limit" to + avoid oversize bodies in bounces. The dafault value matches RFC + limits. + Version 4.86 ------------ diff --git a/src/src/deliver.c b/src/src/deliver.c index 6eb9a65d5..e588ee4a4 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -7401,6 +7401,9 @@ wording. */ if (dsn_ret == dsn_ret_hdrs) topt |= topt_no_body; else + { + struct stat statbuf; + /* no full body return at all? */ if (!bounce_return_body) { @@ -7409,16 +7412,18 @@ wording. */ if (dsn_ret == dsn_ret_full) dsnnotifyhdr = dsnlimitmsg; } + /* line length limited... return headers only if oversize */ /* size limited ... return headers only if limit reached */ - else if (bounce_return_size_limit > 0) - { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) - { - topt |= topt_no_body; - dsnnotifyhdr = dsnlimitmsg; - } + else if ( max_received_linelength > bounce_return_linesize_limit + || ( bounce_return_size_limit > 0 + && fstat(deliver_datafile, &statbuf) == 0 + && statbuf.st_size > max + ) ) + { + topt |= topt_no_body; + dsnnotifyhdr = dsnlimitmsg; } + } #ifdef SUPPORT_I18N if (message_smtputf8) @@ -7748,6 +7753,7 @@ else if (addr_defer != (address_item *)(+1)) FILE *wmf = NULL; FILE *f = fdopen(fd, "wb"); uschar * bound; + int topt; if (warn_message_file) if (!(wmf = Ufopen(warn_message_file, "rb"))) @@ -7894,7 +7900,7 @@ else if (addr_defer != (address_item *)(+1)) fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ - int topt = topt_add_return_path | topt_no_body; + topt = topt_add_return_path | topt_no_body; transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ /* Write the original email out */ diff --git a/src/src/globals.c b/src/src/globals.c index 14a821a19..089d5adb7 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -475,6 +475,7 @@ uschar *bounce_message_file = NULL; uschar *bounce_message_text = NULL; uschar *bounce_recipient = NULL; BOOL bounce_return_body = TRUE; +int bounce_return_linesize_limit = 998; BOOL bounce_return_message = TRUE; int bounce_return_size_limit = 100*1024; uschar *bounce_sender_authentication = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 9ae78a920..bd45e784b 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -250,6 +250,7 @@ extern uschar *bounce_message_file; /* Template file */ extern uschar *bounce_message_text; /* One-liner */ extern uschar *bounce_recipient; /* When writing an errmsg */ extern BOOL bounce_return_body; /* Include body in returned message */ +extern int bounce_return_linesize_limit; /* Max line length in return */ extern BOOL bounce_return_message; /* Include message in bounce */ extern int bounce_return_size_limit; /* Max amount to return */ extern uschar *bounce_sender_authentication; /* AUTH address for bounces */ diff --git a/src/src/moan.c b/src/src/moan.c index e54117c34..7d1a2c681 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -86,7 +86,7 @@ else DEBUG(D_any) debug_printf("Child process %d for sending message\n", pid); /* Creation of child succeeded */ f = fdopen(fd, "wb"); -if (errors_reply_to != NULL) fprintf(f, "Reply-To: %s\n", errors_reply_to); +if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); moan_write_from(f); fprintf(f, "To: %s\n", recipient); @@ -94,140 +94,137 @@ fprintf(f, "To: %s\n", recipient); switch(ident) { case ERRMESS_BADARGADDRESS: - fprintf(f, - "Subject: Mail failure - malformed recipient address\n\n"); - fprintf(f, - "A message that you sent contained a recipient address that was incorrectly\n" - "constructed:\n\n"); - fprintf(f, " %s %s\n", eblock->text1, eblock->text2); - count = Ustrlen(eblock->text1); - if (count > 0 && eblock->text1[count-1] == '.') fprintf(f, - "\nRecipient addresses must not end with a '.' character.\n"); - fprintf(f, - "\nThe message has not been delivered to any recipients.\n"); - break; + "Subject: Mail failure - malformed recipient address\n\n"); + fprintf(f, + "A message that you sent contained a recipient address that was incorrectly\n" + "constructed:\n\n"); + fprintf(f, " %s %s\n", eblock->text1, eblock->text2); + count = Ustrlen(eblock->text1); + if (count > 0 && eblock->text1[count-1] == '.') + fprintf(f, + "\nRecipient addresses must not end with a '.' character.\n"); + fprintf(f, + "\nThe message has not been delivered to any recipients.\n"); + break; case ERRMESS_BADNOADDRESS: case ERRMESS_BADADDRESS: - fprintf(f, - "Subject: Mail failure - malformed recipient address\n\n"); - fprintf(f, - "A message that you sent contained one or more recipient addresses that were\n" - "incorrectly constructed:\n\n"); + fprintf(f, + "Subject: Mail failure - malformed recipient address\n\n"); + fprintf(f, + "A message that you sent contained one or more recipient addresses that were\n" + "incorrectly constructed:\n\n"); - while (eblock != NULL) - { - fprintf(f, " %s: %s\n", eblock->text1, eblock->text2); - count++; - eblock = eblock->next; - } + while (eblock != NULL) + { + fprintf(f, " %s: %s\n", eblock->text1, eblock->text2); + count++; + eblock = eblock->next; + } - fprintf(f, (count == 1)? "\nThis address has been ignored. " : - "\nThese addresses have been ignored. "); + fprintf(f, (count == 1)? "\nThis address has been ignored. " : + "\nThese addresses have been ignored. "); - fprintf(f, (ident == ERRMESS_BADADDRESS)? - "The other addresses in the message were\n" - "syntactically valid and have been passed on for an attempt at delivery.\n" : + fprintf(f, (ident == ERRMESS_BADADDRESS)? + "The other addresses in the message were\n" + "syntactically valid and have been passed on for an attempt at delivery.\n" : - "There were no other addresses in your\n" - "message, and so no attempt at delivery was possible.\n"); - break; + "There were no other addresses in your\n" + "message, and so no attempt at delivery was possible.\n"); + break; case ERRMESS_IGADDRESS: - fprintf(f, "Subject: Mail failure - no recipient addresses\n\n"); - fprintf(f, - "A message that you sent using the -t command line option contained no\n" - "addresses that were not also on the command line, and were therefore\n" - "suppressed. This left no recipient addresses, and so no delivery could\n" - "be attempted.\n"); - break; + fprintf(f, "Subject: Mail failure - no recipient addresses\n\n"); + fprintf(f, + "A message that you sent using the -t command line option contained no\n" + "addresses that were not also on the command line, and were therefore\n" + "suppressed. This left no recipient addresses, and so no delivery could\n" + "be attempted.\n"); + break; case ERRMESS_NOADDRESS: - fprintf(f, "Subject: Mail failure - no recipient addresses\n\n"); - fprintf(f, - "A message that you sent contained no recipient addresses, and therefore no\n" - "delivery could be attempted.\n"); - break; + fprintf(f, "Subject: Mail failure - no recipient addresses\n\n"); + fprintf(f, + "A message that you sent contained no recipient addresses, and therefore no\n" + "delivery could be attempted.\n"); + break; case ERRMESS_IOERR: - fprintf(f, "Subject: Mail failure - system failure\n\n"); - fprintf(f, - "A system failure was encountered while processing a message that you sent,\n" - "so it has not been possible to deliver it. The error was:\n\n%s\n", - eblock->text1); - break; + fprintf(f, "Subject: Mail failure - system failure\n\n"); + fprintf(f, + "A system failure was encountered while processing a message that you sent,\n" + "so it has not been possible to deliver it. The error was:\n\n%s\n", + eblock->text1); + break; case ERRMESS_VLONGHEADER: - fprintf(f, "Subject: Mail failure - overlong header section\n\n"); - fprintf(f, - "A message that you sent contained a header section that was excessively\n" - "long and could not be handled by the mail transmission software. The\n" - "message has not been delivered to any recipients.\n"); - break; + fprintf(f, "Subject: Mail failure - overlong header section\n\n"); + fprintf(f, + "A message that you sent contained a header section that was excessively\n" + "long and could not be handled by the mail transmission software. The\n" + "message has not been delivered to any recipients.\n"); + break; case ERRMESS_VLONGHDRLINE: - fprintf(f, "Subject: Mail failure - overlong header line\n\n"); - fprintf(f, - "A message that you sent contained a header line that was excessively\n" - "long and could not be handled by the mail transmission software. The\n" - "message has not been delivered to any recipients.\n"); - break; + fprintf(f, "Subject: Mail failure - overlong header line\n\n"); + fprintf(f, + "A message that you sent contained a header line that was excessively\n" + "long and could not be handled by the mail transmission software. The\n" + "message has not been delivered to any recipients.\n"); + break; case ERRMESS_TOOBIG: - fprintf(f, "Subject: Mail failure - message too big\n\n"); - fprintf(f, - "A message that you sent was longer than the maximum size allowed on this\n" - "system. It was not delivered to any recipients.\n"); - break; + fprintf(f, "Subject: Mail failure - message too big\n\n"); + fprintf(f, + "A message that you sent was longer than the maximum size allowed on this\n" + "system. It was not delivered to any recipients.\n"); + break; case ERRMESS_TOOMANYRECIP: - fprintf(f, "Subject: Mail failure - too many recipients\n\n"); - fprintf(f, - "A message that you sent contained more recipients than allowed on this\n" - "system. It was not delivered to any recipients.\n"); - break; + fprintf(f, "Subject: Mail failure - too many recipients\n\n"); + fprintf(f, + "A message that you sent contained more recipients than allowed on this\n" + "system. It was not delivered to any recipients.\n"); + break; case ERRMESS_LOCAL_SCAN: case ERRMESS_LOCAL_ACL: - fprintf(f, "Subject: Mail failure - rejected by local scanning code\n\n"); - fprintf(f, - "A message that you sent was rejected by the local scanning code that\n" - "checks incoming messages on this system."); - if (eblock->text1 != NULL) - { + fprintf(f, "Subject: Mail failure - rejected by local scanning code\n\n"); fprintf(f, - " The following error was given:\n\n %s", eblock->text1); - } + "A message that you sent was rejected by the local scanning code that\n" + "checks incoming messages on this system."); + if (eblock->text1) + fprintf(f, " The following error was given:\n\n %s", eblock->text1); fprintf(f, "\n"); break; #ifdef EXPERIMENTAL_DMARC case ERRMESS_DMARC_FORENSIC: - bounce_return_message = TRUE; - bounce_return_body = FALSE; - fprintf(f, + bounce_return_message = TRUE; + bounce_return_body = FALSE; + fprintf(f, "Subject: DMARC Forensic Report for %s from IP %s\n\n", ((eblock == NULL) ? US"Unknown" : eblock->text2), sender_host_address); - fprintf(f, - "A message claiming to be from you has failed the published DMARC\n" - "policy for your domain.\n\n"); - while (eblock != NULL) - { - fprintf(f, " %s: %s\n", eblock->text1, eblock->text2); - count++; - eblock = eblock->next; - } + fprintf(f, + "A message claiming to be from you has failed the published DMARC\n" + "policy for your domain.\n\n"); + while (eblock != NULL) + { + fprintf(f, " %s: %s\n", eblock->text1, eblock->text2); + count++; + eblock = eblock->next; + } break; #endif default: - fprintf(f, "Subject: Mail failure\n\n"); - fprintf(f, - "A message that you sent has caused the error routine to be entered with\n" - "an unknown error number (%d).\n", ident); - break; + fprintf(f, "Subject: Mail failure\n\n"); + fprintf(f, + "A message that you sent has caused the error routine to be entered with\n" + "an unknown error number (%d).\n", ident); + break; } /* Now, if configured, copy the message; first the headers and then the rest of @@ -267,7 +264,7 @@ if (bounce_return_message) /* If the error occurred before the Received: header was created, its text field will still be NULL; just omit such a header line. */ - while (headers != NULL) + while (headers) { if (headers->text != NULL) fprintf(f, "%s", CS headers->text); headers = headers->next; @@ -280,30 +277,47 @@ if (bounce_return_message) in which case we might have to terminate on a line containing just "." as well as on EOF. We may already have the first line in memory. */ - if (bounce_return_body && message_file != NULL) + if (bounce_return_body && message_file) { int ch; - int state = 1; + enum {midline, beginline, haddot} state = beginline; BOOL enddot = dot_ends && message_file == stdin; - if (firstline != NULL) fprintf(f, "%s", CS firstline); - while ((ch = fgetc(message_file)) != EOF) + uschar * buf = store_get(bounce_return_linesize_limit+2); + + if (firstline) fprintf(f, "%s", CS firstline); + + while (fgets(buf, bounce_return_linesize_limit+2, message_file)) { - fputc(ch, f); - if (size_limit > 0 && ++written > size_limit) break; - if (enddot) - { - if (state == 0) { if (ch == '\n') state = 1; } - else if (state == 1) - { if (ch == '.') state = 2; else if (ch != '\n') state = 0; } - else - { if (ch == '\n') break; else state = 0; } - } + int len; + + if (enddot && *buf == '.' && buf[1] == '\n') + { + fputc('.', f); + break; + } + + len = Ustrlen(buf); + if (buf[len-1] != '\n') + { /* eat rest of partial line */ + int ch; + while ((ch = fgetc(message_file)) != EOF && ch != '\n') ; + } + + if (size_limit > 0 && len > size_limit - written) + { + buf[size_limit - written] = '\0'; + fputs(buf, f); + break; + } + + fputs(buf, f); } } #ifdef EXPERIMENTAL_DMARC /* Overkill, but use exact test in case future code gets inserted */ else if (bounce_return_body && message_file == NULL) { + /*XXX limit line length here? */ /* This doesn't print newlines, disable until can parse and fix * output to be legible. */ fprintf(f, "%s", expand_string(US"$message_body")); @@ -365,24 +379,24 @@ moan_to_sender(int ident, error_block *eblock, header_line *headers, uschar *firstline = NULL; uschar *msg = US"Error while reading message with no usable sender address"; -if (message_reference != NULL) +if (message_reference) msg = string_sprintf("%s (R=%s)", msg, message_reference); /* Find the sender from a From line if permitted and possible */ -if (check_sender && message_file != NULL && trusted_caller && +if (check_sender && message_file && trusted_caller && Ufgets(big_buffer, BIG_BUFFER_SIZE, message_file) != NULL) { uschar *new_sender = NULL; if (regex_match_and_setup(regex_From, big_buffer, 0, -1)) new_sender = expand_string(uucp_from_sender); - if (new_sender != NULL) sender_address = new_sender; + if (new_sender) sender_address = new_sender; else firstline = big_buffer; } /* If viable sender address, send a message */ -if (sender_address != NULL && sender_address[0] != 0 && !local_error_message) +if (sender_address && sender_address[0] && !local_error_message) return moan_send_message(sender_address, ident, eblock, headers, message_file, firstline); diff --git a/src/src/readconf.c b/src/src/readconf.c index e52f45fca..f90b66c3d 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -194,6 +194,7 @@ static optionlist optionlist_config[] = { { "bounce_message_file", opt_stringptr, &bounce_message_file }, { "bounce_message_text", opt_stringptr, &bounce_message_text }, { "bounce_return_body", opt_bool, &bounce_return_body }, + { "bounce_return_linesize_limit", opt_mkint, &bounce_return_linesize_limit }, { "bounce_return_message", opt_bool, &bounce_return_message }, { "bounce_return_size_limit", opt_mkint, &bounce_return_size_limit }, { "bounce_sender_authentication",opt_stringptr,&bounce_sender_authentication }, diff --git a/test/confs/0001 b/test/confs/0001 index 2b53c0942..4b7ea85f6 100644 --- a/test/confs/0001 +++ b/test/confs/0001 @@ -38,6 +38,7 @@ bounce_return_body = false no_bounce_return_message return_size_limit = 12K bounce_return_size_limit = 10K +bounce_return_line_limit = 997 callout_domain_negative_expire = 1h callout_domain_positive_expire = 1d callout_negative_expire = 5h diff --git a/test/confs/0573 b/test/confs/0573 new file mode 100644 index 000000000..c3c7a6bea --- /dev/null +++ b/test/confs/0573 @@ -0,0 +1,37 @@ +# Exim test configuration 0573 + +exim_path = EXIM_PATH +hide host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/%slog +gecos_pattern = "" +gecos_name = CALLER_NAME +tls_advertise_hosts = + +# ----- Main settings ----- + +trusted_users = CALLER +bounce_return_linesize_limit = 20 +acl_smtp_rcpt = accept + + +# ----- Routers ----- + +begin routers + +my_main_router: + driver = accept + domains = myhost.test.ex + transport = t1 + +# ----- Transports ----- + +begin transports + +t1: + driver = appendfile + file = DIR/test-mail/$local_part + user = CALLER + +# End diff --git a/test/scripts/0000-Basic/0573 b/test/scripts/0000-Basic/0573 new file mode 100644 index 000000000..88606d23f --- /dev/null +++ b/test/scripts/0000-Basic/0573 @@ -0,0 +1,18 @@ +# bounce_return_linesize_limit +# +exim -odi -f not_limited fred@undeliverable.org +Subject: test + +msg with ok lines +00000000001111111 +01234567890123456 +**** +# +exim -odi -f limited fred@undeliverable.org +Subject: test + +msg with long lines +000000000011111111112222222222 +012345678901234567890123456789 +**** +no_msglog_check -- cgit v1.2.3 From 4dce3152ce5e257dde0575dc2fab4121d127dfb5 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 18 Jan 2016 16:54:45 +0000 Subject: Testuite: tidying --- test/confs/0001 | 2 +- test/runtest | 10 ++++++++-- test/scripts/0000-Basic/0572 | 2 +- test/stdout/0572 | 25 ------------------------- 4 files changed, 10 insertions(+), 29 deletions(-) (limited to 'test/confs') diff --git a/test/confs/0001 b/test/confs/0001 index 4b7ea85f6..5350ba463 100644 --- a/test/confs/0001 +++ b/test/confs/0001 @@ -38,7 +38,7 @@ bounce_return_body = false no_bounce_return_message return_size_limit = 12K bounce_return_size_limit = 10K -bounce_return_line_limit = 997 +bounce_return_linesize_limit = 997 callout_domain_negative_expire = 1h callout_domain_positive_expire = 1d callout_negative_expire = 5h diff --git a/test/runtest b/test/runtest index 18eb4cc4e..2b19f52e3 100755 --- a/test/runtest +++ b/test/runtest @@ -1393,8 +1393,14 @@ $munges = { 'mail' => '/^(X-(Remote-MTA-(smtp-greeting|helo-response)|Exim-Diagnostic|(body|message)-linecount):|Remote-MTA: X-ip;)/' }, - 'optional_dane_ocsp' => - { 'stdout' => '/^hosts_(requ(est|ire)|try)_(dane|ocsp) =/' }, + 'optional_nossl' => + { 'stdout' => '/^( + dkim_(canon|domain|private_key|selector|sign_headers|strict) + |gnutls_require_(kx|mac|protocols) + |hosts_(requ(est|ire)|try)_(dane|ocsp) + |hosts_(avoid|nopass|require|verify_avoid)_tls + |tls_[^ ]* + )($|[ ]=)/x' }, 'sys_bindir' => { 'mainlog' => 's%/(usr/)?bin/%SYSBINDIR/%' }, diff --git a/test/scripts/0000-Basic/0572 b/test/scripts/0000-Basic/0572 index 6bc42fde1..05ba658f7 100644 --- a/test/scripts/0000-Basic/0572 +++ b/test/scripts/0000-Basic/0572 @@ -3,7 +3,7 @@ # Ought to test a non-priv user, checking "hide", but # the testsuite cannot do that... # -munge optional_dane_ocsp +munge optional_nossl exim -bP spool_directory **** perl -e 'print "\n";' diff --git a/test/stdout/0572 b/test/stdout/0572 index 08b69f4e9..240a2332a 100644 --- a/test/stdout/0572 +++ b/test/stdout/0572 @@ -39,12 +39,6 @@ connect_timeout = 5m connection_max_messages = 500 data_timeout = 5m delay_after_cutoff -dkim_canon = -dkim_domain = -dkim_private_key = -dkim_selector = -dkim_sign_headers = -dkim_strict = dns_qualify_single no_dns_search_parents dnssec_request_domains = @@ -53,24 +47,17 @@ dscp = fallback_hosts = final_timeout = 10m no_gethostbyname -gnutls_require_kx = -gnutls_require_mac = -gnutls_require_protocols = helo_data = $primary_hostname hosts = hosts_avoid_esmtp = hosts_avoid_pipelining = -hosts_avoid_tls = hosts_max_try = 5 hosts_max_try_hardlimit = 50 -hosts_nopass_tls = no_hosts_override no_hosts_randomize hosts_require_auth = -hosts_require_tls = hosts_try_auth = hosts_try_prdr = * -hosts_verify_avoid_tls = interface = ip4.ip4.ip4.ip4 keepalive no_lmtp_ignore_quota @@ -82,17 +69,6 @@ retry_include_ip_address serialize_hosts = size_addition = 1024 socks_proxy = 127.0.0.1 port=1225 -tls_certificate = -tls_crl = -tls_dh_min_bits = 1024 -tls_privatekey = -tls_require_ciphers = -tls_sni = -tls_tempfail_tryclear -tls_try_verify_hosts = * -tls_verify_cert_hostnames = * -tls_verify_certificates = system -tls_verify_hosts = # Exim Configuration (X) # 1 "TESTSUITE/test-config" @@ -104,7 +80,6 @@ spool_directory = TESTSUITE/spool log_file_path = TESTSUITE/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME -tls_advertise_hosts = log_selector = +outgoing_port domainlist local_domains = test.ex : *.test.ex acl_smtp_rcpt = accept -- cgit v1.2.3 From 2592e6c0eda522da0f6a33f4d32e33598288eb6e Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 1 Feb 2016 18:18:56 +0000 Subject: DKIM: replace SHA and RSA routines from gnutls, under earlier library versions, using libgcrypt and libtasn1 directly. Bug 1772 --- src/scripts/MakeLinks | 11 +- src/src/dkim.c | 13 +- src/src/dkim.h | 1 + src/src/exim.c | 7 +- src/src/pdkim/Makefile | 9 +- src/src/pdkim/README | 6 +- src/src/pdkim/blob.h | 17 + src/src/pdkim/crypt_ver.h | 12 +- src/src/pdkim/hash.c | 181 ++++++ src/src/pdkim/hash.h | 79 +++ src/src/pdkim/part-x509parse.c | 153 ----- src/src/pdkim/pdkim.c | 534 ++++------------ src/src/pdkim/pdkim.h | 31 +- src/src/pdkim/rsa.c | 679 +++++++++++++++++++++ src/src/pdkim/rsa.h | 81 +++ src/src/pdkim/sha1.c | 323 ---------- src/src/pdkim/sha2.c | 453 -------------- test/aux-fixed/dkim/sign.pl | 4 +- test/confs/4503 | 1 + test/log/4500 | 3 + test/log/4503 | 2 + test/scripts/4500-Domain-Keys-Identified-Mail/4500 | 35 ++ test/scripts/4500-Domain-Keys-Identified-Mail/4503 | 4 +- 23 files changed, 1258 insertions(+), 1381 deletions(-) create mode 100644 src/src/pdkim/blob.h create mode 100644 src/src/pdkim/hash.c create mode 100644 src/src/pdkim/hash.h delete mode 100644 src/src/pdkim/part-x509parse.c create mode 100644 src/src/pdkim/rsa.c create mode 100644 src/src/pdkim/rsa.h delete mode 100644 src/src/pdkim/sha1.c delete mode 100644 src/src/pdkim/sha2.c (limited to 'test/confs') diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 4f6747f7c..886214030 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -82,17 +82,12 @@ cd .. # Likewise for the code for the PDKIM library mkdir pdkim cd pdkim -for f in README Makefile crypt_ver.h part-x509parse.c pdkim.c \ - pdkim.h sha1.c sha2.c +for f in README Makefile crypt_ver.h pdkim.c \ + pdkim.h hash.c hash.h rsa.c rsa.h blob.h do ln -s ../../src/pdkim/$f $f done -mkdir polarssl -cd polarssl -for i in `ls ../../../src/pdkim/polarssl/` ; do - ln -s ../../../src/pdkim/polarssl/$i $i -done -cd ../.. +cd .. # The basic source files for Exim and utilities. NB local_scan.h gets linked, # but local_scan.c does not, because its location is taken from the build-time diff --git a/src/src/dkim.c b/src/src/dkim.c index 36b103e8f..349947ab1 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -59,6 +59,13 @@ return PDKIM_FAIL; } +void +dkim_exim_init(void) +{ +pdkim_init(); +} + + void dkim_exim_verify_init(void) { @@ -129,7 +136,7 @@ for (sig = dkim_signatures; sig; sig = sig->next) sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1", - sig->sigdata_len * 8 + sig->sigdata.len * 8 ), sig->identity ? string_sprintf("i=%s ", sig->identity) : US"", @@ -255,7 +262,7 @@ for (sig = dkim_signatures; sig; sig = sig->next) dkim_signing_domain = US sig->domain; dkim_signing_selector = US sig->selector; - dkim_key_length = sig->sigdata_len * 8; + dkim_key_length = sig->sigdata.len * 8; return; } } @@ -340,7 +347,7 @@ switch (what) case DKIM_HEADERNAMES: return dkim_cur_sig->headernames - ? US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what); + ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what); case DKIM_IDENTITY: return dkim_cur_sig->identity diff --git a/src/src/dkim.h b/src/src/dkim.h index 39a0408a1..1655f8e45 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge, 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +void dkim_exim_init(void); uschar *dkim_exim_sign(int, uschar *, const uschar *, uschar *, uschar *, uschar *); void dkim_exim_verify_init(void); void dkim_exim_verify_feed(uschar *, int); diff --git a/src/src/exim.c b/src/src/exim.c index 28617a510..ebc71dd37 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1760,7 +1760,6 @@ regex_whitelisted_macro = for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; - /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ @@ -4556,6 +4555,12 @@ if (list_config) } +/* Initialise subsystems as required */ +#ifndef DISABLE_DKIM +dkim_exim_init(); +#endif + + /* Handle a request to deliver one or more messages that are already on the queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with above. MSG_LOAD is handled with -be (which is the only time it applies) below. diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile index ec8bb8305..c72a9426b 100644 --- a/src/src/pdkim/Makefile +++ b/src/src/pdkim/Makefile @@ -1,6 +1,6 @@ # Make file for building the pdkim library. -OBJ = pdkim.o +OBJ = pdkim.o hash.o rsa.o pdkim.a: $(OBJ) @$(RM_COMMAND) -f pdkim.a @@ -12,9 +12,8 @@ pdkim.a: $(OBJ) .c.o:; @echo "$(CC) $*.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -I. $*.c -part-x509parse.o: $(HDRS) crypt_ver.h part-x509parse.c -pdkim.o: $(HDRS) crypt_ver.h pdkim.h pdkim.c -sha1.o: $(HDRS) crypt_ver.h sha1.c -sha2.o: $(HDRS) crypt_ver.h sha2.c +pdkim.o: $(HDRS) crypt_ver.h hash.h blob.h pdkim.h pdkim.c +hash.o: $(HDRS) crypt_ver.h hash.h blob.h pdkim.h hash.c +rsa.o: $(HDRS) crypt_ver.h rsa.h blob.h rsa.c # End diff --git a/src/src/pdkim/README b/src/src/pdkim/README index de04cff2d..953e86eae 100644 --- a/src/src/pdkim/README +++ b/src/src/pdkim/README @@ -2,10 +2,8 @@ PDKIM - a RFC4871 (DKIM) implementation http://duncanthrax.net/pdkim/ Copyright (C) 2009 Tom Kistner -Includes code from the PolarSSL project. -http://polarssl.org -Copyright (C) 2009 Paul Bakker -Copyright (C) 2006-2008 Christophe Devine +No longer includes code from the PolarSSL project. +Copyright (C) 2016 Jeremy Harris This copy of PDKIM is included with Exim. For a standalone distribution, visit http://duncanthrax.net/pdkim/. diff --git a/src/src/pdkim/blob.h b/src/src/pdkim/blob.h new file mode 100644 index 000000000..e1481c9f4 --- /dev/null +++ b/src/src/pdkim/blob.h @@ -0,0 +1,17 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2016 Exim maintainers + * + * RSA signing/verification interface + */ + +#ifndef BLOB_H /* entire file */ +#define BLOB_H + +typedef struct { + uschar * data; + size_t len; +} blob; + +#endif diff --git a/src/src/pdkim/crypt_ver.h b/src/src/pdkim/crypt_ver.h index 602d137a3..2a9dde952 100644 --- a/src/src/pdkim/crypt_ver.h +++ b/src/src/pdkim/crypt_ver.h @@ -11,14 +11,18 @@ #ifdef USE_GNUTLS -# define RSA_GNUTLS - # include + +# if GNUTLS_VERSION_NUMBER > 0x020c00 +# define RSA_GNUTLS +# else +# define RSA_GCRYPT +# endif + # if GNUTLS_VERSION_NUMBER >= 0x020a00 # define SHA_GNUTLS - # else -# define SHA_POLARSSL +# define SHA_GCRYPT # endif #else diff --git a/src/src/pdkim/hash.c b/src/src/pdkim/hash.c new file mode 100644 index 000000000..0751683e4 --- /dev/null +++ b/src/src/pdkim/hash.c @@ -0,0 +1,181 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2016 Exim maintainers + * + * Hash interface functions + */ + +#include "../exim.h" + +#ifndef DISABLE_DKIM /* entire file */ + +#ifndef SUPPORT_TLS +# error Need SUPPORT_TLS for DKIM +#endif + +#include "crypt_ver.h" + +#ifdef RSA_OPENSSL +# include +# include +# include +#elif defined(RSA_GNUTLS) +# include +# include +# ifdef RSA_VERIFY_GNUTLS +# include +# endif +#endif + +#ifdef SHA_GNUTLS +# include +#endif + +#include "hash.h" + + +/******************************************************************************/ +#ifdef SHA_OPENSSL + +void +exim_sha_init(hctx * h, BOOL sha1) +{ +h->sha1 = sha1; +h->hashlen = sha1 ? 20 : 32; +if (h->sha1) + SHA1_Init (&h->u.sha1); +else + SHA256_Init(&h->u.sha2); +} + + +void +exim_sha_update(hctx * h, const char * data, int len) +{ +if (h->sha1) + SHA1_Update (&h->u.sha1, data, len); +else + SHA256_Update(&h->u.sha2, data, len); +} + + +void +exim_sha_finish(hctx * h, blob * b) +{ +b->data = store_get(b->len = h->hashlen); + +if (h->sha1) + SHA1_Final (b->data, &h->u.sha1); +else + SHA256_Final(b->data, &h->u.sha2); +} + + + +#elif defined(SHA_GNUTLS) +/******************************************************************************/ + +void +exim_sha_init(hctx * h, BOOL sha1) +{ +h->sha1 = sha1; +h->hashlen = sha1 ? 20 : 32; +gnutls_hash_init(&h->sha, sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256); +} + + +void +exim_sha_update(hctx * h, const char * data, int len) +{ +gnutls_hash(h->sha, data, len); +} + + +void +exim_sha_finish(hctx * h, blob * b) +{ +b->data = store_get(b->len = h->hashlen); +gnutls_hash_output(h->sha, b->data); +} + + + +#elif defined(SHA_GCRYPT) +/******************************************************************************/ + +void +exim_sha_init(hctx * h, BOOL sha1) +{ +h->sha1 = sha1; +h->hashlen = sha1 ? 20 : 32; +gcry_md_open(&h->sha, sha1 ? GCRY_MD_SHA1 : GCRY_MD_SHA256, 0); +} + + +void +exim_sha_update(hctx * h, const char * data, int len) +{ +gcry_md_write(h->sha, data, len); +} + + +void +exim_sha_finish(hctx * h, blob * b) +{ +b->data = store_get(b->len = h->hashlen); +memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen); +} + + + + +#elif defined(SHA_POLARSSL) +/******************************************************************************/ + +void +exim_sha_init(hctx * h, BOOL sha1) +{ +h->sha1 = sha1; +h->hashlen = sha1 ? 20 : 32; +if (h->sha1) + sha1_starts(&h->u.sha1); +else + sha2_starts(&h->u.sha2, 0); +} + + +void +exim_sha_update(hctx * h, const char * data, int len) +{ +if (h->sha1) + sha1_update(h->u.sha1, US data, len); +else + sha2_update(h->u.sha2, US data, len); +} + + +void +exim_sha_finish(hctx * h, blob * b) +{ +b->data = store_get(b->len = h->hashlen); + +if (h->sha1) + sha1_finish(h->u.sha1, b->data); +else + sha2_finish(h->u.sha2, b->data); +} + +#endif +/******************************************************************************/ + +/* Common to all library versions */ +int +exim_sha_hashlen(hctx * h) +{ +return h->sha1 ? 20 : 32; +} + + +#endif /*DISABLE_DKIM*/ +/* End of File */ diff --git a/src/src/pdkim/hash.h b/src/src/pdkim/hash.h new file mode 100644 index 000000000..afd7ea6a6 --- /dev/null +++ b/src/src/pdkim/hash.h @@ -0,0 +1,79 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2016 Exim maintainers + * + * Hash interface functions + */ + +#include "../exim.h" + +#if !defined(DISABLE_DKIM) && !defined(PDKIM_HASH_H) /* entire file */ +#define PDKIM_HASH_H + +#ifndef SUPPORT_TLS +# error Need SUPPORT_TLS for DKIM +#endif + +#include "crypt_ver.h" +#include "blob.h" + +#ifdef RSA_OPENSSL +# include +# include +# include +#elif defined(RSA_GNUTLS) +# include +# include +#endif + +#ifdef SHA_GNUTLS +# include +#elif defined(SHA_GCRYPT) +# include +#elif defined(SHA_POLARSSL) +# include "pdkim.h" +# include "polarssl/sha1.h" +# include "polarssl/sha2.h" +#endif + +/* Hash context */ +typedef struct { + int sha1; + int hashlen; + +#ifdef SHA_OPENSSL + union { + SHA_CTX sha1; /* SHA1 block */ + SHA256_CTX sha2; /* SHA256 block */ + } u; + +#elif defined(SHA_GNUTLS) + gnutls_hash_hd_t sha; /* Either SHA1 or SHA256 block */ + +#elif defined(SHA_GCRYPT) + gcry_md_hd_t sha; /* Either SHA1 or SHA256 block */ + +#elif defined(SHA_POLARSSL) + union { + sha1_context sha1; /* SHA1 block */ + sha2_context sha2; /* SHA256 block */ + } u; +#endif + +} hctx; + +#if defined(SHA_OPENSSL) +# include "pdkim.h" +#elif defined(SHA_GCRYPT) +# include "pdkim.h" +#endif + + +extern void exim_sha_init(hctx *, BOOL); +extern void exim_sha_update(hctx *, const char *a, int); +extern void exim_sha_finish(hctx *, blob *); +extern int exim_sha_hashlen(hctx *); + +#endif /*DISABLE_DKIM*/ +/* End of File */ diff --git a/src/src/pdkim/part-x509parse.c b/src/src/pdkim/part-x509parse.c deleted file mode 100644 index 5788777f2..000000000 --- a/src/src/pdkim/part-x509parse.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "crypt_ver.h" - -#ifdef SHA_POLARSSL /* remainder of file */ - -#include "polarssl/bignum.h" -#include "polarssl/part-x509.h" -#include "polarssl/private-x509parse_c.h" - -/* all calls are from src/pdkim/pdkim-rsa.c */ - -/* *************** begin copy from x509parse.c ********************/ -/* - * X.509 certificate and private key decoding - * - * Copyright (C) 2006-2010, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * The ITU-T X.509 standard defines a certificat format for PKI. - * - * http://www.ietf.org/rfc/rfc2459.txt - * http://www.ietf.org/rfc/rfc3279.txt - * - * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc - * - * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf - * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf - */ - - -/* - * ASN.1 DER decoding routines - */ -static int asn1_get_len( unsigned char **p, - const unsigned char *end, - int *len ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( ( **p & 0x80 ) == 0 ) - *len = *(*p)++; - else - { - switch( **p & 0x7F ) - { - case 1: - if( ( end - *p ) < 2 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = (*p)[1]; - (*p) += 2; - break; - - case 2: - if( ( end - *p ) < 3 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = ( (*p)[1] << 8 ) | (*p)[2]; - (*p) += 3; - break; - - default: - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - break; - } - } - - if( *len > (int) ( end - *p ) ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - return( 0 ); -} - -/* This function is not exported by PolarSSL 0.14.2 - * static */ -int asn1_get_tag( unsigned char **p, - const unsigned char *end, - int *len, int tag ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( **p != tag ) - return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - (*p)++; - - return( asn1_get_len( p, end, len ) ); -} - -/* This function is not exported by PolarSSL 0.14.2 - * static */ -int asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ) -{ - int ret, len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - if( len > (int) sizeof( int ) || ( **p & 0x80 ) != 0 ) - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - *val = 0; - - while( len-- > 0 ) - { - *val = ( *val << 8 ) | **p; - (*p)++; - } - - return( 0 ); -} - -/* This function is not exported by PolarSSL 0.14.2 - * static */ -int asn1_get_mpi( unsigned char **p, - const unsigned char *end, - mpi *X ) -{ - int ret, len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - ret = mpi_read_binary( X, *p, len ); - - *p += len; - - return( ret ); -} -/* *************** end copy from x509parse.c ********************/ -#endif diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 6e471a614..789d650e6 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -39,17 +39,10 @@ #elif defined(RSA_GNUTLS) # include # include -# include -#endif - -#ifdef SHA_GNUTLS -# include -#elif defined(SHA_POLARSSL) -# include "polarssl/sha1.h" -# include "polarssl/sha2.h" #endif #include "pdkim.h" +#include "rsa.h" #define PDKIM_SIGNATURE_VERSION "1" #define PDKIM_PUB_RECORD_VERSION "DKIM1" @@ -155,8 +148,8 @@ pdkim_verify_ext_status_str(int ext_status) /* -------------------------------------------------------------------------- */ /* Print debugging functions */ -void -pdkim_quoteprint(const char *data, int len, int lf) +static void +pdkim_quoteprint(const char *data, int len) { int i; const unsigned char *p = (const unsigned char *)data; @@ -180,20 +173,18 @@ for (i = 0; i < len; i++) break; } } -if (lf) - debug_printf("\n"); +debug_printf("\n"); } -void -pdkim_hexprint(const char *data, int len, int lf) +static void +pdkim_hexprint(const char *data, int len) { int i; const unsigned char *p = (const unsigned char *)data; for (i = 0 ; i < len; i++) debug_printf("%02x", p[i]); -if (lf) - debug_printf("\n"); +debug_printf("\n"); } @@ -318,7 +309,6 @@ if (pub) if (pub->keytype ) free(pub->keytype); if (pub->srvtype ) free(pub->srvtype); if (pub->notes ) free(pub->notes); -/* if (pub->key ) free(pub->key); */ free(pub); } } @@ -345,7 +335,6 @@ if (sig) if (sig->selector ) free(sig->selector); if (sig->domain ) free(sig->domain); if (sig->identity ) free(sig->identity); - if (sig->headernames ) free(sig->headernames); if (sig->copiedheaders ) free(sig->copiedheaders); if (sig->rsa_privkey ) free(sig->rsa_privkey); if (sig->sign_headers ) free(sig->sign_headers); @@ -560,37 +549,26 @@ return n; /* -------------------------------------------------------------------------- */ -static char * -pdkim_decode_base64(char *str, int *num_decoded) +static void +pdkim_decode_base64(uschar *str, blob * b) { -int dlen = 0; +int dlen; char *res; -int old_pool = store_pool; - -/* There is a store-reset between header & body reception -so cannot use the main pool */ - -store_pool = POOL_PERM; -dlen = b64decode(US str, USS &res); -store_pool = old_pool; - -if (dlen < 0) return NULL; - -if (num_decoded) *num_decoded = dlen; -return res; +dlen = b64decode(str, &b->data); +if (dlen < 0) b->data = NULL; +b->len = dlen; } - /* -------------------------------------------------------------------------- */ static char * -pdkim_encode_base64(char *str, int num) +pdkim_encode_base64(blob * b) { char * ret; int old_pool = store_pool; store_pool = POOL_PERM; -ret = CS b64encode(US str, num); +ret = CS b64encode(b->data, b->len); store_pool = old_pool; return ret; } @@ -612,6 +590,13 @@ BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; int i; +int old_pool = store_pool; + +/* There is a store-reset between header & body reception +so cannot use the main pool. Any allocs done by Exim +memory-handling must use the perm pool. */ + +store_pool = POOL_PERM; if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL; memset(sig, 0, sizeof(pdkim_signature)); @@ -689,11 +674,9 @@ for (p = raw_hdr; ; p++) { case 'b': if (cur_tag->str[1] == 'h') - sig->bodyhash = pdkim_decode_base64(cur_val->str, - &sig->bodyhash_len); + pdkim_decode_base64(US cur_val->str, &sig->bodyhash); else - sig->sigdata = pdkim_decode_base64(cur_val->str, - &sig->sigdata_len); + pdkim_decode_base64(US cur_val->str, &sig->sigdata); break; case 'v': /* We only support version 1, and that is currently the @@ -739,7 +722,7 @@ for (p = raw_hdr; ; p++) case 'l': sig->bodylength = strtol(cur_val->str, NULL, 10); break; case 'h': - sig->headernames = strdup(cur_val->str); break; + sig->headernames = string_copy(cur_val->str); break; case 'z': sig->copiedheaders = pdkim_decode_qp(cur_val->str); break; default: @@ -764,12 +747,12 @@ NEXT_CHAR: *q++ = c; } +store_pool = old_pool; + /* Make sure the most important bits are there. */ if (!(sig->domain && (*(sig->domain) != '\0') && sig->selector && (*(sig->selector) != '\0') && sig->headernames && (*(sig->headernames) != '\0') && - sig->bodyhash && - sig->sigdata && sig->version)) { pdkim_free_sig(sig); @@ -786,38 +769,14 @@ DEBUG(D_acl) { debug_printf( "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val), 1); + pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val)); debug_printf( - "PDKIM >> Sig size: %4d bits\n", sig->sigdata_len*8); + "PDKIM >> Sig size: %4d bits\n", sig->sigdata.len*8); debug_printf( "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } -#ifdef SHA_OPENSSL - -SHA1_Init (&sig->sha1_body); -SHA256_Init(&sig->sha2_body); - -#elif defined(SHA_GNUTLS) - -gnutls_hash_init(&sig->sha_body, - sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256); - -#elif defined(SHA_POLARSSL) - -if ( !(sig->sha1_body = malloc(sizeof(sha1_context))) - || !(sig->sha2_body = malloc(sizeof(sha2_context))) - ) - { - pdkim_free_sig(sig); - return NULL; - } - -sha1_starts(sig->sha1_body); -sha2_starts(sig->sha2_body, 0); - -#endif /* SHA impl */ - +exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1); return sig; } @@ -898,7 +857,8 @@ for (p = raw_record; ; p++) case 'n': pub->notes = pdkim_decode_qp(cur_val->str); break; case 'p': - pub->key = pdkim_decode_base64(cur_val->str, &(pub->key_len)); break; + pdkim_decode_base64(US cur_val->str, &pub->key); + break; case 'k': pub->hashes = strdup(cur_val->str); break; case 's': @@ -931,7 +891,7 @@ if (!pub->keytype ) pub->keytype = strdup("rsa"); if (!pub->srvtype ) pub->srvtype = strdup("*"); /* p= is required */ -if (pub->key) +if (pub->key.data) return pub; pdkim_free_pubkey(pub); @@ -1002,23 +962,9 @@ while (sig) if (canon_len > 0) { -#ifdef SHA_GNUTLS - gnutls_hash(sig->sha_body, canon_data, canon_len); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - SHA1_Update (&sig->sha1_body, canon_data, canon_len); - else - SHA256_Update(&sig->sha2_body, canon_data, canon_len); -# elif defined(SHA_POLARSSL) - sha1_update(sig->sha1_body, US canon_data, canon_len); - else - sha2_update(sig->sha2_body, US canon_data, canon_len); -# endif -#endif - + exim_sha_update(&sig->body_hash, canon_data, canon_len); sig->signed_body_bytes += canon_len; - DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len, 1); + DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len); } sig = sig->next; @@ -1039,36 +985,22 @@ pdkim_signature *sig; /* Traverse all signatures */ for (sig = ctx->sig; sig; sig = sig->next) { /* Finish hashes */ - uschar bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */ - -#ifdef SHA_GNUTLS - gnutls_hash_output(sig->sha_body, bh); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - SHA1_Final (bh, &sig->sha1_body); - else - SHA256_Final(bh, &sig->sha2_body); -# elif defined(SHA_POLARSSL) - sha1_finish(sig->sha1_body, bh); - else - sha2_finish(sig->sha2_body, bh); -# endif -#endif + blob bh; + + exim_sha_finish(&sig->body_hash, &bh); DEBUG(D_acl) { debug_printf("PDKIM [%s] Body bytes hashed: %lu\n" "PDKIM [%s] bh computed: ", sig->domain, sig->signed_body_bytes, sig->domain); - pdkim_hexprint((char *)bh, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1); + pdkim_hexprint(CS bh.data, bh.len); } /* SIGNING -------------------------------------------------------------- */ if (ctx->mode == PDKIM_MODE_SIGN) { - sig->bodyhash_len = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32; - sig->bodyhash = CS string_copyn(US bh, sig->bodyhash_len); + sig->bodyhash = bh; /* If bodylength limit is set, and we have received less bytes than the requested amount, effectively remove the limit tag. */ @@ -1080,8 +1012,7 @@ for (sig = ctx->sig; sig; sig = sig->next) else { /* Compare bodyhash */ - if (memcmp(bh, sig->bodyhash, - (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) + if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0) { DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain); } @@ -1090,8 +1021,8 @@ for (sig = ctx->sig; sig; sig = sig->next) DEBUG(D_acl) { debug_printf("PDKIM [%s] bh signature: ", sig->domain); - pdkim_hexprint(sig->bodyhash, - sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1); + pdkim_hexprint(sig->bodyhash.data, + exim_sha_hashlen(&sig->body_hash)); debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain); } sig->verify_status = PDKIM_VERIFY_FAIL; @@ -1466,7 +1397,7 @@ return str->str; /* -------------------------------------------------------------------------- */ static char * -pdkim_create_header(pdkim_signature *sig, int final) +pdkim_create_header(pdkim_signature *sig, BOOL final) { char *rc = NULL; char *base64_bh = NULL; @@ -1481,7 +1412,7 @@ if (!(hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION))) if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers]))) goto BAIL; -if (!(base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len))) +if (!(base64_bh = pdkim_encode_base64(&sig->bodyhash))) goto BAIL; col = strlen(hdr->str); @@ -1496,9 +1427,9 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo]) && pdkim_headcat(&col, hdr, ";", "s=", sig->selector) ) { - /* list of eader names can be split between items. */ + /* list of header names can be split between items. */ { - char *n = strdup(sig->headernames); + char *n = CS string_copy(sig->headernames); char *f = n; char *i = "h="; char *s = ";"; @@ -1513,13 +1444,11 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo]) if (!i) if (!pdkim_headcat(&col, hdr, NULL, NULL, ":")) { - free(f); goto BAIL; } if (!pdkim_headcat(&col, hdr, s, i, n)) { - free(f); goto BAIL; } @@ -1530,7 +1459,6 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo]) s = NULL; i = NULL; } - free(f); } if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh)) @@ -1571,7 +1499,7 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo]) /* Preliminary or final version? */ if (final) { - if (!(base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len))) + if (!(base64_b = pdkim_encode_base64(&sig->sigdata))) goto BAIL; if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b)) goto BAIL; @@ -1627,40 +1555,17 @@ if (ctx->mode == PDKIM_MODE_SIGN) while (sig) { -#ifdef SHA_OPENSSL - SHA_CTX sha1_headers; - SHA256_CTX sha2_headers; -#elif defined(SHA_GNUTLS) - gnutls_hash_hd_t sha_headers; -#elif defined(SHA_POLARSSL) - sha1_context sha1_headers; - sha2_context sha2_headers; -#endif - - char *sig_hdr; - char headerhash[32]; - -#ifdef RSA_GNUTLS - uschar * hdata = NULL; + BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1; + hctx hhash_ctx; + char * sig_hdr; + blob hhash; + blob hdata; int hdata_alloc = 0; - int hdata_size = 0; -#endif -#ifdef SHA_GNUTLS - gnutls_hash_init(&sha_headers, - sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - SHA1_Init(&sha1_headers); - else - SHA256_Init(&sha2_headers); -# elif defined(SHA_POLARSSL) - sha1_starts(&sha1_headers); - else - sha2_starts(&sha2_headers, 0); -# endif -#endif + hdata.data = NULL; + hdata.len = 0; + + exim_sha_init(&hhash_ctx, is_sha1); DEBUG(D_acl) debug_printf( "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n"); @@ -1675,43 +1580,27 @@ while (sig) for (p = sig->headers; p; p = p->next) { - char *rh = NULL; + uschar * rh; /* Collect header names (Note: colon presence is guaranteed here) */ - char *q = strchr(p->value, ':'); + uschar * q = Ustrchr(p->value, ':'); if (!(pdkim_strncat(headernames, p->value, - (q-(p->value)) + (p->next ? 1 : 0)))) + (q-US (p->value)) + (p->next ? 1 : 0)))) return PDKIM_ERR_OOM; rh = sig->canon_headers == PDKIM_CANON_RELAXED - ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */ - : strdup(p->value); /* just copy it for simple canon */ + ? US pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */ + : string_copy(p->value); /* just copy it for simple canon */ if (!rh) return PDKIM_ERR_OOM; /* Feed header to the hash algorithm */ -#ifdef SHA_GNUTLS - gnutls_hash(sha_headers, rh, strlen(rh)); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - SHA1_Update (&sha1_headers, rh, strlen(rh)); - else - SHA256_Update(&sha2_headers, rh, strlen(rh)); -# elif defined(SHA_POLARSSL) - sha1_update(&sha1_headers, US rh, strlen(rh)); - else - sha2_update(&sha2_headers, US rh, strlen(rh)); -# endif -#endif + exim_sha_update(&hhash_ctx, rh, strlen(rh)); -#ifdef RSA_GNUTLS - /* Remember headers block for signing */ - hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, rh); -#endif + /* Remember headers block for signing (when the library cannot do incremental) */ + (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh); - DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1); - free(rh); + DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh)); } } @@ -1720,10 +1609,10 @@ while (sig) add the headers to the hash in that order. */ else { - char *b = strdup(sig->headernames); - char *p = b; - char *q = NULL; - pdkim_stringlist *hdrs; + uschar * b = string_copy(sig->headernames); + uschar * p = b; + uschar * q; + pdkim_stringlist * hdrs; if (!b) return PDKIM_ERR_OOM; @@ -1733,41 +1622,25 @@ while (sig) while(1) { - if ((q = strchr(p, ':'))) + if ((q = Ustrchr(p, ':'))) *q = '\0'; for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next) if ( hdrs->tag == 0 - && strncasecmp(hdrs->value, p, strlen(p)) == 0 - && (hdrs->value)[strlen(p)] == ':' + && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0 + && (hdrs->value)[Ustrlen(p)] == ':' ) { - char *rh; - - rh = sig->canon_headers == PDKIM_CANON_RELAXED - ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */ - : strdup(hdrs->value); /* just copy it for simple canon */ + uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED + ? US pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */ + : string_copy(hdrs->value); /* just copy it for simple canon */ if (!rh) return PDKIM_ERR_OOM; /* Feed header to the hash algorithm */ -#ifdef SHA_GNUTLS - gnutls_hash(sha_headers, rh, strlen(rh)); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - SHA1_Update (&sha1_headers, rh, strlen(rh)); - else - SHA256_Update(&sha2_headers, rh, strlen(rh)); -# elif defined(SHA_POLARSSL) - sha1_update(&sha1_headers, US rh, strlen(rh)); - else - sha2_update(&sha2_headers, US rh, strlen(rh)); -# endif -#endif + exim_sha_update(&hhash_ctx, rh, strlen(rh)); - DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1); - free(rh); + DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh)); hdrs->tag = 1; break; } @@ -1775,7 +1648,6 @@ while (sig) if (!q) break; p = q+1; } - free(b); } DEBUG(D_acl) debug_printf( @@ -1785,11 +1657,11 @@ while (sig) if (ctx->mode == PDKIM_MODE_SIGN) { /* Copy headernames to signature struct */ - sig->headernames = strdup(headernames->str); + sig->headernames = string_copy(US headernames->str); pdkim_strfree(headernames); /* Create signature header with b= omitted */ - sig_hdr = pdkim_create_header(ctx->sig, 0); + sig_hdr = pdkim_create_header(ctx->sig, FALSE); } /* VERIFICATION ----------------------------------------------------------- */ @@ -1815,172 +1687,71 @@ while (sig) { debug_printf( "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n"); - pdkim_quoteprint(sig_hdr, strlen(sig_hdr), 1); + pdkim_quoteprint(sig_hdr, strlen(sig_hdr)); debug_printf( "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } /* Finalize header hash */ -#ifdef SHA_GNUTLS - gnutls_hash(sha_headers, sig_hdr, strlen(sig_hdr)); - gnutls_hash_output(sha_headers, headerhash); -#else - if (sig->algo == PDKIM_ALGO_RSA_SHA1) -# ifdef SHA_OPENSSL - { - SHA1_Update(&sha1_headers, sig_hdr, strlen(sig_hdr)); - SHA1_Final(US headerhash, &sha1_headers); - } - else - { - SHA256_Update(&sha2_headers, sig_hdr, strlen(sig_hdr)); - SHA256_Final(US headerhash, &sha2_headers); - } -# elif defined(SHA_POLARSSL) - { - sha1_update(&sha1_headers, US sig_hdr, strlen(sig_hdr)); - sha1_finish(&sha1_headers, US headerhash); - } - else - { - sha2_update(&sha2_headers, US sig_hdr, strlen(sig_hdr)); - sha2_finish(&sha2_headers, US headerhash); - } -# endif -#endif + exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr)); + exim_sha_finish(&hhash_ctx, &hhash); DEBUG(D_acl) { debug_printf("PDKIM [%s] hh computed: ", sig->domain); - pdkim_hexprint(headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32, 1); + pdkim_hexprint(hhash.data, hhash.len); } -#ifdef RSA_GNUTLS + /* Remember headers block for signing (when the library cannot do incremental) */ if (ctx->mode == PDKIM_MODE_SIGN) - hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, sig_hdr); -#endif + (void) exim_rsa_data_append(&hdata, &hdata_alloc, sig_hdr); free(sig_hdr); /* SIGNING ---------------------------------------------------------------- */ if (ctx->mode == PDKIM_MODE_SIGN) { -#ifdef RSA_OPENSSL - RSA * rsa; - uschar * p, * q; - int len; -#elif defined(RSA_GNUTLS) - gnutls_x509_privkey_t rsa; - gnutls_datum_t k; - int rc; - size_t sigsize; -#endif + es_ctx sctx; + const uschar * errstr; /* Import private key */ -#ifdef RSA_OPENSSL - - if ( !(p = Ustrstr(sig->rsa_privkey, "-----BEGIN RSA PRIVATE KEY-----")) - || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----")) - ) - return PDKIM_SIGN_PRIVKEY_WRAP; - *q = '\0'; - if ((len = b64decode(p, &p)) < 0) - { - DEBUG(D_acl) debug_printf("b64decode failed\n"); - return PDKIM_SIGN_PRIVKEY_B64D; - } - if (!(rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len))) - { - DEBUG(D_acl) - { - char ssl_errstring[256]; - ERR_error_string(ERR_get_error(), ssl_errstring); - debug_printf("d2i_RSAPrivateKey: %s\n", ssl_errstring); - } - return PDKIM_ERR_RSA_PRIVKEY; - } - -#elif defined(RSA_GNUTLS) - - k.data = sig->rsa_privkey; - k.size = strlen(sig->rsa_privkey); - if ( (rc = gnutls_x509_privkey_init(&rsa)) != GNUTLS_E_SUCCESS - || (rc = gnutls_x509_privkey_import2(rsa, &k, - GNUTLS_X509_FMT_PEM, NULL, GNUTLS_PKCS_PLAIN)) != GNUTLS_E_SUCCESS - ) + if ((errstr = exim_rsa_signing_init(sig->rsa_privkey, &sctx))) { - DEBUG(D_acl) debug_printf("gnutls_x509_privkey_import2: %s\n", - gnutls_strerror(rc)); + DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr); return PDKIM_ERR_RSA_PRIVKEY; } -#endif - + /* Do signing. With OpenSSL we are signing the hash of headers just + calculated, with GnuTLS we have to sign an entire block of headers + (due to available interfaces) and it recalculates the hash internally. */ - /* Allocate mem for signature */ -#ifdef RSA_OPENSSL - sig->sigdata = store_get(RSA_size(rsa)); -#elif defined(RSA_GNUTLS) - k.data = hdata; - k.size = hdata_size; - (void) gnutls_x509_privkey_sign_data(rsa, - sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, - 0, &k, NULL, &sigsize); - sig->sigdata = store_get(sig->sigdata_len = sigsize); +#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT) + hdata = hhash; #endif - /* Do signing */ -#ifdef RSA_OPENSSL - - if (RSA_sign(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256, - CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, - US sig->sigdata, (unsigned int *)&sig->sigdata_len, - rsa) != 1) - return PDKIM_ERR_RSA_SIGNING; - RSA_free(rsa); - -#elif defined(RSA_GNUTLS) - - if ((rc = gnutls_x509_privkey_sign_data(rsa, - sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, - 0, &k, sig->sigdata, &sigsize)) != GNUTLS_E_SUCCESS - ) + if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata))) { - DEBUG(D_acl) debug_printf("gnutls_x509_privkey_sign_data: %s\n", - gnutls_strerror(rc)); + DEBUG(D_acl) debug_printf("signing: %s\n", errstr); return PDKIM_ERR_RSA_SIGNING; } - gnutls_x509_privkey_deinit(rsa); - -#endif - DEBUG(D_acl) { debug_printf( "PDKIM [%s] b computed: ", sig->domain); - pdkim_hexprint(sig->sigdata, sig->sigdata_len, 1); + pdkim_hexprint(sig->sigdata.data, sig->sigdata.len); } - if (!(sig->signature_header = pdkim_create_header(ctx->sig, 1))) + if (!(sig->signature_header = pdkim_create_header(ctx->sig, TRUE))) return PDKIM_ERR_OOM; } /* VERIFICATION ----------------------------------------------------------- */ else { -#ifdef RSA_OPENSSL - RSA * rsa; - const unsigned char * p; -#elif defined(RSA_GNUTLS) - gnutls_pubkey_t rsa; - gnutls_datum_t k, s; - int rc; -#endif - char *dns_txt_name, *dns_txt_reply; + ev_ctx vctx; + const uschar * errstr; -#ifdef RSA_GNUTLS - gnutls_pubkey_init(&rsa); -#endif + char *dns_txt_name, *dns_txt_reply; /* Fetch public key for signing domain, from DNS */ @@ -2016,9 +1787,9 @@ while (sig) DEBUG(D_acl) { debug_printf( - "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" - " Raw record: "); - pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply), 1); + "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + " Raw record: "); + pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply)); } if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply))) @@ -2033,69 +1804,27 @@ while (sig) } DEBUG(D_acl) debug_printf( - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); /* Import public key */ -#ifdef RSA_OPENSSL - - p = CUS sig->pubkey->key; - if (!(rsa = d2i_RSA_PUBKEY(NULL, &p, (long) sig->pubkey->key_len))) - -#elif defined(RSA_GNUTLS) - - k.data = sig->pubkey->key; - k.size = sig->pubkey->key_len; - if ((rc = gnutls_pubkey_import(rsa, &k, GNUTLS_X509_FMT_DER)) - != GNUTLS_E_SUCCESS) - -#endif + if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx))) { - DEBUG(D_acl) - { -#ifdef RSA_OPENSSL - long e; - ERR_load_crypto_strings(); /*XXX move to a startup routine */ - while ((e = ERR_get_error())) - debug_printf("Az: %.120s\n", ERR_error_string(e, NULL)); -#elif defined(RSA_GNUTLS) - debug_printf("gnutls_pubkey_import: %s\n", gnutls_strerror(rc)); -#endif - } - + DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr); sig->verify_status = PDKIM_VERIFY_INVALID; sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT; goto NEXT_VERIFY; } /* Check the signature */ -#ifdef RSA_OPENSSL - - if (RSA_verify(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256, - CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, - US sig->sigdata, (unsigned int)sig->sigdata_len, - rsa) != 1) - -#elif defined(RSA_GNUTLS) - - k.data = headerhash; - k.size = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32; - s.data = sig->sigdata; - s.size = sig->sigdata_len; - if ((rc = gnutls_pubkey_verify_hash2(rsa, - sig->algo == PDKIM_ALGO_RSA_SHA1 - ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256, - 0, &k, &s)) < 0) - -#endif + if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata))) { -#if defined(RSA_GNUTLS) - debug_printf("gnutls_pubkey_verify_hash2: %s\n", gnutls_strerror(rc)); -#endif + DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr); sig->verify_status = PDKIM_VERIFY_FAIL; sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE; goto NEXT_VERIFY; } + /* We have a winner! (if bodydhash was correct earlier) */ if (sig->verify_status == PDKIM_VERIFY_NONE) sig->verify_status = PDKIM_VERIFY_PASS; @@ -2113,9 +1842,6 @@ NEXT_VERIFY: debug_printf("\n"); } -#ifdef RSA_GNUTLS - gnutls_pubkey_deinit(rsa); -#endif free(dns_txt_name); free(dns_txt_reply); } @@ -2189,33 +1915,15 @@ sig->bodylength = -1; ctx->mode = PDKIM_MODE_SIGN; ctx->sig = sig; -ctx->sig->domain = strdup(domain); -ctx->sig->selector = strdup(selector); -ctx->sig->rsa_privkey = strdup(rsa_privkey); -ctx->sig->algo = algo; - -if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) - goto BAIL; - -#ifdef SHA_OPENSSL -SHA1_Init (&ctx->sig->sha1_body); -SHA256_Init(&ctx->sig->sha2_body); - -#elif defined(SHA_GNUTLS) -gnutls_hash_init(&ctx->sig->sha_body, - algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256); - -#elif defined(SHA_POLARSSL) -if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context)))) - goto BAIL; -sha1_starts(ctx->sig->sha1_body); +sig->domain = strdup(domain); +sig->selector = strdup(selector); +sig->rsa_privkey = strdup(rsa_privkey); +sig->algo = algo; -if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context)))) +if (!sig->domain || !sig->selector || !sig->rsa_privkey) goto BAIL; -sha2_starts(ctx->sig->sha2_body, 0); - -#endif +exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1); return ctx; BAIL: @@ -2254,4 +1962,12 @@ return PDKIM_OK; } +void +pdkim_init(void) +{ +exim_rsa_init(); +} + + + #endif /*DISABLE_DKIM*/ diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index 313afd503..4de0a7ad6 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -19,6 +19,11 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#ifndef PDKIM_H +#define PDKIM_H + +#include "blob.h" +#include "hash.h" /* -------------------------------------------------------------------------- */ /* Length of the preallocated buffer for the "answer" from the dns/txt @@ -98,8 +103,7 @@ typedef struct pdkim_pubkey { char *srvtype; /* s= */ char *notes; /* n= */ - char *key; /* p= */ - int key_len; + blob key; /* p= */ int testing; /* t=y */ int no_subdomaining; /* t=s */ @@ -151,18 +155,16 @@ typedef struct pdkim_signature { /* (h=) Colon-separated list of header names that are included in the signature */ - char *headernames; + uschar *headernames; /* (z=) */ char *copiedheaders; /* (b=) Raw signature data, along with its length in bytes */ - char *sigdata; - int sigdata_len; + blob sigdata; /* (bh=) Raw body hash data, along with its length in bytes */ - char *bodyhash; - int bodyhash_len; + blob bodyhash; /* Folded DKIM-Signature: header. Singing only, NULL for verifying. Ready for insertion into the message. Note: Folded using CRLFTB, @@ -228,15 +230,8 @@ typedef struct pdkim_signature { /* Properties below this point are used internally only ------------- */ /* Per-signature helper variables ----------------------------------- */ -#ifdef SHA_OPENSSL - SHA_CTX sha1_body; /* SHA1 block */ - SHA256_CTX sha2_body; /* SHA256 block */ -#elif defined(SHA_GNUTLS) - gnutls_hash_hd_t sha_body; /* Either SHA1 or SHA256 block */ -#elif defined(SHA_POLARSSL) - sha1_context *sha1_body; /* SHA1 block */ - sha2_context *sha2_body; /* SHA256 block */ -#endif + hctx body_hash; + unsigned long signed_body_bytes; /* How many body bytes we hashed */ pdkim_stringlist *headers; /* Raw headers included in the sig */ /* Signing specific ------------------------------------------------- */ @@ -283,6 +278,8 @@ typedef struct pdkim_ctx { extern "C" { #endif +void pdkim_init (void); + DLLEXPORT pdkim_ctx *pdkim_init_sign (char *, char *, char *, int); @@ -306,3 +303,5 @@ void pdkim_free_ctx (pdkim_ctx *); #ifdef __cplusplus } #endif + +#endif diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c new file mode 100644 index 000000000..c5d4c2efa --- /dev/null +++ b/src/src/pdkim/rsa.c @@ -0,0 +1,679 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2016 Exim maintainers + * + * RSA signing/verification interface + */ + +#include "../exim.h" + +#ifndef DISABLE_DKIM /* entire file */ + +#ifndef SUPPORT_TLS +# error Need SUPPORT_TLS for DKIM +#endif + +#include "crypt_ver.h" +#include "rsa.h" + + +/******************************************************************************/ +#ifdef RSA_GNUTLS + +void +exim_rsa_init(void) +{ +} + + +/* accumulate data (gnutls-only). String to be appended must be nul-terminated. */ +blob * +exim_rsa_data_append(blob * b, int * alloc, uschar * s) +{ +int len = b->len; +b->data = string_append(b->data, alloc, &len, 1, s); +b->len = len; +return b; +} + + + +/* import private key from PEM string in memory. +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx) +{ +gnutls_datum_t k; +int rc; + +k.data = privkey_pem; +k.size = strlen(privkey_pem); + +if ( (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS + /*|| (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k, + GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS */ + ) + return gnutls_strerror(rc); + +if ( /* (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS + ||*/ (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k, + GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS + ) + return gnutls_strerror(rc); + +return NULL; +} + + + +/* allocate mem for signature (when signing) */ +/* sign data (gnutls_only) +OR +sign hash. + +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig) +{ +gnutls_datum_t k; +size_t sigsize = 0; +int rc; +const uschar * ret = NULL; + +/* Allocate mem for signature */ +k.data = data->data; +k.size = data->len; +(void) gnutls_x509_privkey_sign_data(sign_ctx->rsa, + is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, + 0, &k, NULL, &sigsize); + +sig->data = store_get(sigsize); +sig->len = sigsize; + +/* Do signing */ +if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa, + is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, + 0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS + ) + ret = gnutls_strerror(rc); + +gnutls_x509_privkey_deinit(sign_ctx->rsa); +return ret; +} + + + +/* import public key (from DER in memory) +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx) +{ +gnutls_datum_t k; +int rc; +const uschar * ret = NULL; + +gnutls_pubkey_init(&verify_ctx->rsa); + +k.data = pubkey_der->data; +k.size = pubkey_der->len; + +if ((rc = gnutls_pubkey_import(verify_ctx->rsa, &k, GNUTLS_X509_FMT_DER)) + != GNUTLS_E_SUCCESS) + ret = gnutls_strerror(rc); +return ret; +} + + +/* verify signature (of hash) (given pubkey & alleged sig) +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig) +{ +gnutls_datum_t k, s; +int rc; +const uschar * ret = NULL; + +k.data = data_hash->data; +k.size = data_hash->len; +s.data = sig->data; +s.size = sig->len; +if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->rsa, + is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256, + 0, &k, &s)) < 0) + ret = gnutls_strerror(rc); + +gnutls_pubkey_deinit(verify_ctx->rsa); +return ret; +} + + + + +#elif defined(RSA_GCRYPT) +/******************************************************************************/ + + +/* Internal service routine: +Read and move past an asn.1 header, checking class & tag, +optionally returning the data-length */ + +static int +as_tag(blob * der, uschar req_cls, long req_tag, long * alen) +{ +int rc; +uschar tag_class; +int taglen; +long tag, len; + +/* debug_printf("as_tag: %02x %02x %02x %02x\n", + der->data[0], der->data[1], der->data[2], der->data[3]); */ + +if ((rc = asn1_get_tag_der(der->data++, der->len--, &tag_class, &taglen, &tag)) + != ASN1_SUCCESS) + return rc; + +if (tag_class != req_cls || tag != req_tag) return ASN1_ELEMENT_NOT_FOUND; + +if ((len = asn1_get_length_der(der->data, der->len, &taglen)) < 0) + return ASN1_DER_ERROR; +if (alen) *alen = len; + +/* debug_printf("as_tag: tlen %d dlen %d\n", taglen, (int)len); */ + +der->data += taglen; +der->len -= taglen; +return rc; +} + +/* Internal service routine: +Read and move over an asn.1 integer, setting an MPI to the value +*/ + +static uschar * +as_mpi(blob * der, gcry_mpi_t * mpi) +{ +long alen; +int rc; +gcry_error_t gerr; + +/* integer; move past the header */ +if ((rc = as_tag(der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS) + return US asn1_strerror(rc); + +/* read to an MPI */ +if ((gerr = gcry_mpi_scan(mpi, GCRYMPI_FMT_STD, der->data, alen, NULL))) + return US gcry_strerror(gerr); + +/* move over the data */ +der->data += alen; der->len -= alen; +return NULL; +} + + + +void +exim_rsa_init(void) +{ +/* Version check should be the very first call because it +makes sure that important subsystems are initialized. */ +if (!gcry_check_version (GCRYPT_VERSION)) + { + fputs ("libgcrypt version mismatch\n", stderr); + exit (2); + } + +/* We don't want to see any warnings, e.g. because we have not yet +parsed program options which might be used to suppress such +warnings. */ +gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + +/* ... If required, other initialization goes here. Note that the +process might still be running with increased privileges and that +the secure memory has not been initialized. */ + +/* Allocate a pool of 16k secure memory. This make the secure memory +available and also drops privileges where needed. */ +gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + +/* It is now okay to let Libgcrypt complain when there was/is +a problem with the secure memory. */ +gcry_control (GCRYCTL_RESUME_SECMEM_WARN); + +/* ... If required, other initialization goes here. */ + +/* Tell Libgcrypt that initialization has completed. */ +gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + +return; +} + + + + +/* Accumulate data (gnutls-only). +String to be appended must be nul-terminated. */ + +blob * +exim_rsa_data_append(blob * b, int * alloc, uschar * s) +{ +return b; /*dummy*/ +} + + + +/* import private key from PEM string in memory. +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx) +{ +uschar * s1, * s2; +blob der; +long alen; +int rc; + +/* + * RSAPrivateKey ::= SEQUENCE + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + */ + +if ( !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----")) + || !(s2 = Ustrstr(CS (s1+=31), "-----END RSA PRIVATE KEY-----" )) + ) + return US"Bad PEM wrapper"; + +*s2 = '\0'; + +if ((der.len = b64decode(s1, &der.data)) < 0) + return US"Bad PEM-DER b64 decode"; + +/* untangle asn.1 */ + +/* sequence; just move past the header */ +if ((rc = as_tag(&der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL)) + != ASN1_SUCCESS) goto asn_err; + +/* integer version; move past the header, check is zero */ +if ((rc = as_tag(&der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS) + goto asn_err; +if (alen != 1 || *der.data != 0) + return US"Bad version number"; +der.data++; der.len--; + +if ( (s1 = as_mpi(&der, &sign_ctx->n)) + || (s1 = as_mpi(&der, &sign_ctx->e)) + || (s1 = as_mpi(&der, &sign_ctx->d)) + || (s1 = as_mpi(&der, &sign_ctx->p)) + || (s1 = as_mpi(&der, &sign_ctx->q)) + || (s1 = as_mpi(&der, &sign_ctx->dp)) + || (s1 = as_mpi(&der, &sign_ctx->dq)) + || (s1 = as_mpi(&der, &sign_ctx->qp)) + ) + return s1; + +DEBUG(D_acl) debug_printf("rsa_signing_init:\n"); + { + uschar * s; + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->n); + debug_printf(" N : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->e); + debug_printf(" E : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->d); + debug_printf(" D : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->p); + debug_printf(" P : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->q); + debug_printf(" Q : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dp); + debug_printf(" DP: %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dq); + debug_printf(" DQ: %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->qp); + debug_printf(" QP: %s\n", s); + } +return NULL; + +asn_err: return US asn1_strerror(rc); +} + + + +/* allocate mem for signature (when signing) */ +/* sign data (gnutls_only) +OR +sign hash. + +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig) +{ +gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL; +gcry_mpi_t m_sig; +uschar * errstr; +gcry_error_t gerr; + +#define SIGSPACE 128 +sig->data = store_get(SIGSPACE); + +if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0) + { + gcry_mpi_swap (sign_ctx->p, sign_ctx->q); + gcry_mpi_invm (sign_ctx->qp, sign_ctx->p, sign_ctx->q); + } + +if ( (gerr = gcry_sexp_build (&s_key, NULL, + "(private-key (rsa (n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + sign_ctx->n, sign_ctx->e, + sign_ctx->d, sign_ctx->p, + sign_ctx->q, sign_ctx->qp)) + || (gerr = gcry_sexp_build (&s_hash, NULL, + is_sha1 + ? "(data(flags pkcs1)(hash sha1 %b))" + : "(data(flags pkcs1)(hash sha256 %b))", + (int) data->len, CS data->data)) + || (gerr = gcry_pk_sign (&s_sig, s_hash, s_key)) + ) + return US gcry_strerror(gerr); + +/* gcry_sexp_dump(s_sig); */ + +if ( !(s_sig = gcry_sexp_find_token(s_sig, "s", 0)) + ) + return US"no sig result"; + +m_sig = gcry_sexp_nth_mpi(s_sig, 1, GCRYMPI_FMT_USG); + +DEBUG(D_acl) + { + uschar * s; + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, m_sig); + debug_printf(" SG: %s\n", s); + } + +gerr = gcry_mpi_print(GCRYMPI_FMT_USG, sig->data, SIGSPACE, &sig->len, m_sig); +if (gerr) + { + debug_printf("signature conversion from MPI to buffer failed\n"); + return US gcry_strerror(gerr); + } +#undef SIGSPACE + +return NULL; +} + + +/* import public key (from DER in memory) +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx) +{ +/* +in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi() +*/ +uschar tag_class; +int taglen; +long alen; +int rc; +uschar * errstr; +gcry_error_t gerr; +uschar * stage = US"S1"; + +/* +sequence + sequence + OBJECT:rsaEncryption + NULL + BIT STRING:RSAPublicKey + sequence + INTEGER:Public modulus + INTEGER:Public exponent + +openssl rsa -in aux-fixed/dkim/dkim.private -pubout -outform DER | od -t x1 | head; +openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump; +openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump -offset 22; +*/ + +/* sequence; just move past the header */ +if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL)) + != ASN1_SUCCESS) goto asn_err; + +/* sequence; skip the entire thing */ +DEBUG(D_acl) stage = US"S2"; +if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, &alen)) + != ASN1_SUCCESS) goto asn_err; +pubkey_der->data += alen; pubkey_der->len -= alen; + + +/* bitstring: limit range to size of bitstring; +move over header + content wrapper */ +DEBUG(D_acl) stage = US"BS"; +if ((rc = as_tag(pubkey_der, 0, ASN1_TAG_BIT_STRING, &alen)) != ASN1_SUCCESS) + goto asn_err; +pubkey_der->len = alen; +pubkey_der->data++; pubkey_der->len--; + +/* sequence; just move past the header */ +DEBUG(D_acl) stage = US"S3"; +if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL)) + != ASN1_SUCCESS) goto asn_err; + +/* read two integers */ +DEBUG(D_acl) stage = US"MPI"; +if ( (errstr = as_mpi(pubkey_der, &verify_ctx->n)) + || (errstr = as_mpi(pubkey_der, &verify_ctx->e)) + ) + return errstr; + +DEBUG(D_acl) debug_printf("rsa_verify_init:\n"); + { + uschar * s; + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->n); + debug_printf(" N : %s\n", s); + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->e); + debug_printf(" E : %s\n", s); + } + +return NULL; + +asn_err: +DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc)); + return US asn1_strerror(rc); +} + + +/* verify signature (of hash) (given pubkey & alleged sig) +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig) +{ +/* +cf. libgnutls 2.8.5 _wrap_gcry_pk_verify() +*/ +gcry_mpi_t m_sig; +gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; +gcry_error_t gerr; +uschar * stage; + +if ( (stage = US"pkey sexp build", + gerr = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", + verify_ctx->n, verify_ctx->e)) + || (stage = US"data sexp build", + gerr = gcry_sexp_build (&s_hash, NULL, + is_sha1 + ? "(data(flags pkcs1)(hash sha1 %b))" + : "(data(flags pkcs1)(hash sha256 %b))", + (int) data_hash->len, CS data_hash->data)) + || (stage = US"sig mpi scan", + gerr = gcry_mpi_scan(&m_sig, GCRYMPI_FMT_USG, sig->data, sig->len, NULL)) + || (stage = US"sig sexp build", + gerr = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", m_sig)) + || (stage = US"verify", + gerr = gcry_pk_verify (s_sig, s_hash, s_pkey)) + ) + { + DEBUG(D_acl) debug_printf("verify: error in stage '%s'\n", stage); + return US gcry_strerror(gerr); + } + +if (s_sig) gcry_sexp_release (s_sig); +if (s_hash) gcry_sexp_release (s_hash); +if (s_pkey) gcry_sexp_release (s_pkey); +gcry_mpi_release (m_sig); +gcry_mpi_release (verify_ctx->n); +gcry_mpi_release (verify_ctx->e); + +return NULL; +} + + + + +#elif defined(RSA_OPENSSL) +/******************************************************************************/ + +void +exim_rsa_init(void) +{ +} + + +/* accumulate data (gnutls-only) */ +blob * +exim_rsa_data_append(blob * b, int * alloc, uschar * s) +{ +return b; /*dummy*/ +} + + +/* import private key from PEM string in memory. +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx) +{ +uschar * p, * q; +int len; + +/* Convert PEM to DER */ +if ( !(p = Ustrstr(privkey_pem, "-----BEGIN RSA PRIVATE KEY-----")) + || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----")) + ) + return US"Bad PEM wrapping"; + +*q = '\0'; +if ((len = b64decode(p, &p)) < 0) + return US"b64decode failed"; + +if (!(sign_ctx->rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len))) + { + char ssl_errstring[256]; + ERR_load_crypto_strings(); /*XXX move to a startup routine */ + ERR_error_string(ERR_get_error(), ssl_errstring); + return string_copy(ssl_errstring); + } + +return NULL; +} + + + +/* allocate mem for signature (when signing) */ +/* sign data (gnutls_only) +OR +sign hash. + +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig) +{ +uint len; +const uschar * ret = NULL; + +/* Allocate mem for signature */ +len = RSA_size(sign_ctx->rsa); +sig->data = store_get(len); +sig->len = len; + +/* Do signing */ +if (RSA_sign(is_sha1 ? NID_sha1 : NID_sha256, + CUS data->data, data->len, + US sig->data, &len, sign_ctx->rsa) != 1) + { + char ssl_errstring[256]; + ERR_load_crypto_strings(); /*XXX move to a startup routine */ + ERR_error_string(ERR_get_error(), ssl_errstring); + ret = string_copy(ssl_errstring); + } + +RSA_free(sign_ctx->rsa); +return ret;; +} + + + +/* import public key (from DER in memory) +Return: nULL for success, or an error string */ + +const uschar * +exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx) +{ +const uschar * p = CUS pubkey_der->data; +const uschar * ret = NULL; + +if (!(verify_ctx->rsa = d2i_RSA_PUBKEY(NULL, &p, (long) pubkey_der->len))) + { + char ssl_errstring[256]; + ERR_load_crypto_strings(); /*XXX move to a startup routine */ + ERR_error_string(ERR_get_error(), ssl_errstring); + ret = string_copy(ssl_errstring); + } +return ret; +} + + + + +/* verify signature (of hash) (given pubkey & alleged sig) +Return: NULL for success, or an error string */ + +const uschar * +exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig) +{ +const uschar * ret = NULL; + +if (RSA_verify(is_sha1 ? NID_sha1 : NID_sha256, + CUS data_hash->data, data_hash->len, + US sig->data, (uint) sig->len, verify_ctx->rsa) != 1) + { + char ssl_errstring[256]; + ERR_load_crypto_strings(); /*XXX move to a startup routine */ + ERR_error_string(ERR_get_error(), ssl_errstring); + ret = string_copy(ssl_errstring); + } +return ret; +} + + +#endif +/******************************************************************************/ + +#endif /*DISABLE_DKIM*/ +/* End of File */ diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h new file mode 100644 index 000000000..32631fdac --- /dev/null +++ b/src/src/pdkim/rsa.h @@ -0,0 +1,81 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2016 Exim maintainers + * + * RSA signing/verification interface + */ + +#include "../exim.h" + +#ifndef DISABLE_DKIM /* entire file */ + +#include "crypt_ver.h" + +#ifdef RSA_OPENSSL +# include +# include +# include +#elif defined(RSA_GNUTLS) +# include +# include +# include +#elif defined(RSA_GCRYPT) +# include +# include +#endif + +#include "blob.h" + + +#ifdef RSA_OPENSSL + +typedef struct { + RSA * rsa; +} es_ctx; + +typedef struct { + RSA * rsa; +} ev_ctx; + +#elif defined(RSA_GNUTLS) + +typedef struct { + gnutls_x509_privkey_t rsa; +} es_ctx; + +typedef struct { + gnutls_pubkey_t rsa; +} ev_ctx; + +#elif defined(RSA_GCRYPT) + +typedef struct { + gcry_mpi_t n; + gcry_mpi_t e; + gcry_mpi_t d; + gcry_mpi_t p; + gcry_mpi_t q; + gcry_mpi_t dp; + gcry_mpi_t dq; + gcry_mpi_t qp; +} es_ctx; + +typedef struct { + gcry_mpi_t n; + gcry_mpi_t e; +} ev_ctx; + +#endif + + +extern void exim_rsa_init(void); +extern blob * exim_rsa_data_append(blob *, int *, uschar *); + +extern const uschar * exim_rsa_signing_init(uschar *, es_ctx *); +extern const uschar * exim_rsa_sign(es_ctx *, BOOL, blob *, blob *); +extern const uschar * exim_rsa_verify_init(blob *, ev_ctx *); +extern const uschar * exim_rsa_verify(ev_ctx *, BOOL, blob *, blob *); + +#endif /*DISABLE_DKIM*/ +/* End of File */ diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c deleted file mode 100644 index 96f4e7b57..000000000 --- a/src/src/pdkim/sha1.c +++ /dev/null @@ -1,323 +0,0 @@ -#include "crypt_ver.h" - -#ifdef SHA_POLARSSL /* remainder of file */ - -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Copyright (C) 2006-2010, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * The SHA-1 standard was published by NIST in 1993. - * - * http://www.itl.nist.gov/fipspubs/fip180-1.htm - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SHA1_C) - -#include "polarssl/sha1.h" - -#include -#include - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_ULONG_BE -#define GET_ULONG_BE(n,b,i) \ -{ \ - (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ - | ( (unsigned long) (b)[(i) + 1] << 16 ) \ - | ( (unsigned long) (b)[(i) + 2] << 8 ) \ - | ( (unsigned long) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_ULONG_BE -#define PUT_ULONG_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ -} -#endif - -/* - * SHA-1 context setup - * Called from pdkim_parse_sig_header() pdkim_feed_finish() pdkim_init_sign() - */ -void sha1_starts( sha1_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; -} - -static void sha1_process( sha1_context *ctx, const unsigned char data[64] ) -{ - unsigned long temp, W[16], A, B, C, D, E; - - GET_ULONG_BE( W[ 0], data, 0 ); - GET_ULONG_BE( W[ 1], data, 4 ); - GET_ULONG_BE( W[ 2], data, 8 ); - GET_ULONG_BE( W[ 3], data, 12 ); - GET_ULONG_BE( W[ 4], data, 16 ); - GET_ULONG_BE( W[ 5], data, 20 ); - GET_ULONG_BE( W[ 6], data, 24 ); - GET_ULONG_BE( W[ 7], data, 28 ); - GET_ULONG_BE( W[ 8], data, 32 ); - GET_ULONG_BE( W[ 9], data, 36 ); - GET_ULONG_BE( W[10], data, 40 ); - GET_ULONG_BE( W[11], data, 44 ); - GET_ULONG_BE( W[12], data, 48 ); - GET_ULONG_BE( W[13], data, 52 ); - GET_ULONG_BE( W[14], data, 56 ); - GET_ULONG_BE( W[15], data, 60 ); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define R(t) \ -( \ - temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ - W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ - ( W[t & 0x0F] = S(temp,1) ) \ -) - -#define P(a,b,c,d,e,x) \ -{ \ - e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - -#define F(x,y,z) (z ^ (x & (y ^ z))) -#define K 0x5A827999 - - P( A, B, C, D, E, W[0] ); - P( E, A, B, C, D, W[1] ); - P( D, E, A, B, C, W[2] ); - P( C, D, E, A, B, W[3] ); - P( B, C, D, E, A, W[4] ); - P( A, B, C, D, E, W[5] ); - P( E, A, B, C, D, W[6] ); - P( D, E, A, B, C, W[7] ); - P( C, D, E, A, B, W[8] ); - P( B, C, D, E, A, W[9] ); - P( A, B, C, D, E, W[10] ); - P( E, A, B, C, D, W[11] ); - P( D, E, A, B, C, W[12] ); - P( C, D, E, A, B, W[13] ); - P( B, C, D, E, A, W[14] ); - P( A, B, C, D, E, W[15] ); - P( E, A, B, C, D, R(16) ); - P( D, E, A, B, C, R(17) ); - P( C, D, E, A, B, R(18) ); - P( B, C, D, E, A, R(19) ); - -#undef K -#undef F - -#define F(x,y,z) (x ^ y ^ z) -#define K 0x6ED9EBA1 - - P( A, B, C, D, E, R(20) ); - P( E, A, B, C, D, R(21) ); - P( D, E, A, B, C, R(22) ); - P( C, D, E, A, B, R(23) ); - P( B, C, D, E, A, R(24) ); - P( A, B, C, D, E, R(25) ); - P( E, A, B, C, D, R(26) ); - P( D, E, A, B, C, R(27) ); - P( C, D, E, A, B, R(28) ); - P( B, C, D, E, A, R(29) ); - P( A, B, C, D, E, R(30) ); - P( E, A, B, C, D, R(31) ); - P( D, E, A, B, C, R(32) ); - P( C, D, E, A, B, R(33) ); - P( B, C, D, E, A, R(34) ); - P( A, B, C, D, E, R(35) ); - P( E, A, B, C, D, R(36) ); - P( D, E, A, B, C, R(37) ); - P( C, D, E, A, B, R(38) ); - P( B, C, D, E, A, R(39) ); - -#undef K -#undef F - -#define F(x,y,z) ((x & y) | (z & (x | y))) -#define K 0x8F1BBCDC - - P( A, B, C, D, E, R(40) ); - P( E, A, B, C, D, R(41) ); - P( D, E, A, B, C, R(42) ); - P( C, D, E, A, B, R(43) ); - P( B, C, D, E, A, R(44) ); - P( A, B, C, D, E, R(45) ); - P( E, A, B, C, D, R(46) ); - P( D, E, A, B, C, R(47) ); - P( C, D, E, A, B, R(48) ); - P( B, C, D, E, A, R(49) ); - P( A, B, C, D, E, R(50) ); - P( E, A, B, C, D, R(51) ); - P( D, E, A, B, C, R(52) ); - P( C, D, E, A, B, R(53) ); - P( B, C, D, E, A, R(54) ); - P( A, B, C, D, E, R(55) ); - P( E, A, B, C, D, R(56) ); - P( D, E, A, B, C, R(57) ); - P( C, D, E, A, B, R(58) ); - P( B, C, D, E, A, R(59) ); - -#undef K -#undef F - -#define F(x,y,z) (x ^ y ^ z) -#define K 0xCA62C1D6 - - P( A, B, C, D, E, R(60) ); - P( E, A, B, C, D, R(61) ); - P( D, E, A, B, C, R(62) ); - P( C, D, E, A, B, R(63) ); - P( B, C, D, E, A, R(64) ); - P( A, B, C, D, E, R(65) ); - P( E, A, B, C, D, R(66) ); - P( D, E, A, B, C, R(67) ); - P( C, D, E, A, B, R(68) ); - P( B, C, D, E, A, R(69) ); - P( A, B, C, D, E, R(70) ); - P( E, A, B, C, D, R(71) ); - P( D, E, A, B, C, R(72) ); - P( C, D, E, A, B, R(73) ); - P( B, C, D, E, A, R(74) ); - P( A, B, C, D, E, R(75) ); - P( E, A, B, C, D, R(76) ); - P( D, E, A, B, C, R(77) ); - P( C, D, E, A, B, R(78) ); - P( B, C, D, E, A, R(79) ); - -#undef K -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; -} - -/* - * SHA-1 process buffer - * Called from pdkim_feed_finish() & pdkim_finish_bodyhash() - */ -void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ) -{ - int fill; - unsigned long left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (unsigned long) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, fill ); - sha1_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - sha1_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, ilen ); - } -} - -static const unsigned char sha1_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * SHA-1 final digest - * Called from pdkim_feed_finish() & pdkim_finish_bodyhash() - */ -void sha1_finish( sha1_context *ctx, unsigned char output[20] ) -{ - unsigned long last, padn; - unsigned long high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_ULONG_BE( high, msglen, 0 ); - PUT_ULONG_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - sha1_update( ctx, (unsigned char *) sha1_padding, padn ); - sha1_update( ctx, msglen, 8 ); - - PUT_ULONG_BE( ctx->state[0], output, 0 ); - PUT_ULONG_BE( ctx->state[1], output, 4 ); - PUT_ULONG_BE( ctx->state[2], output, 8 ); - PUT_ULONG_BE( ctx->state[3], output, 12 ); - PUT_ULONG_BE( ctx->state[4], output, 16 ); -} - -#endif -#endif diff --git a/src/src/pdkim/sha2.c b/src/src/pdkim/sha2.c deleted file mode 100644 index 6ab6cb906..000000000 --- a/src/src/pdkim/sha2.c +++ /dev/null @@ -1,453 +0,0 @@ -#include "crypt_ver.h" - -#ifdef SHA_POLARSSL /* remainder of file */ - -/* - * FIPS-180-2 compliant SHA-256 implementation - * - * Copyright (C) 2006-2010, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * The SHA-256 Secure Hash Standard was published by NIST in 2002. - * - * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SHA2_C) - -#include "polarssl/sha2.h" - -#include -#include - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_ULONG_BE -#define GET_ULONG_BE(n,b,i) \ -{ \ - (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ - | ( (unsigned long) (b)[(i) + 1] << 16 ) \ - | ( (unsigned long) (b)[(i) + 2] << 8 ) \ - | ( (unsigned long) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_ULONG_BE -#define PUT_ULONG_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ -} -#endif - -/* - * SHA-256 context setup - */ -void sha2_starts( sha2_context *ctx, int is224 ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - if( is224 == 0 ) - { - /* SHA-256 */ - ctx->state[0] = 0x6A09E667; - ctx->state[1] = 0xBB67AE85; - ctx->state[2] = 0x3C6EF372; - ctx->state[3] = 0xA54FF53A; - ctx->state[4] = 0x510E527F; - ctx->state[5] = 0x9B05688C; - ctx->state[6] = 0x1F83D9AB; - ctx->state[7] = 0x5BE0CD19; - } - else - { - /* SHA-224 */ - ctx->state[0] = 0xC1059ED8; - ctx->state[1] = 0x367CD507; - ctx->state[2] = 0x3070DD17; - ctx->state[3] = 0xF70E5939; - ctx->state[4] = 0xFFC00B31; - ctx->state[5] = 0x68581511; - ctx->state[6] = 0x64F98FA7; - ctx->state[7] = 0xBEFA4FA4; - } - - ctx->is224 = is224; -} - -static void sha2_process( sha2_context *ctx, const unsigned char data[64] ) -{ - unsigned long temp1, temp2, W[64]; - unsigned long A, B, C, D, E, F, G, H; - - GET_ULONG_BE( W[ 0], data, 0 ); - GET_ULONG_BE( W[ 1], data, 4 ); - GET_ULONG_BE( W[ 2], data, 8 ); - GET_ULONG_BE( W[ 3], data, 12 ); - GET_ULONG_BE( W[ 4], data, 16 ); - GET_ULONG_BE( W[ 5], data, 20 ); - GET_ULONG_BE( W[ 6], data, 24 ); - GET_ULONG_BE( W[ 7], data, 28 ); - GET_ULONG_BE( W[ 8], data, 32 ); - GET_ULONG_BE( W[ 9], data, 36 ); - GET_ULONG_BE( W[10], data, 40 ); - GET_ULONG_BE( W[11], data, 44 ); - GET_ULONG_BE( W[12], data, 48 ); - GET_ULONG_BE( W[13], data, 52 ); - GET_ULONG_BE( W[14], data, 56 ); - GET_ULONG_BE( W[15], data, 60 ); - -#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) -#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) - -#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) -#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) - -#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) -#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) - -#define F0(x,y,z) ((x & y) | (z & (x | y))) -#define F1(x,y,z) (z ^ (x & (y ^ z))) - -#define R(t) \ -( \ - W[t] = S1(W[t - 2]) + W[t - 7] + \ - S0(W[t - 15]) + W[t - 16] \ -) - -#define P(a,b,c,d,e,f,g,h,x,K) \ -{ \ - temp1 = h + S3(e) + F1(e,f,g) + K + x; \ - temp2 = S2(a) + F0(a,b,c); \ - d += temp1; h = temp1 + temp2; \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - F = ctx->state[5]; - G = ctx->state[6]; - H = ctx->state[7]; - - P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); - P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); - P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); - P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); - P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); - P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); - P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); - P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); - P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); - P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); - P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); - P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); - P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); - P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); - P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); - P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); - P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); - P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); - P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); - P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); - P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); - P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); - P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); - P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); - P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); - P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); - P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); - P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); - P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); - P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); - P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); - P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); - P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); - P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); - P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); - P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); - P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); - P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); - P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); - P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); - P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); - P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); - P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); - P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); - P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); - P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); - P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); - P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); - P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); - P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); - P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); - P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); - P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); - P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); - P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); - P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); - P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); - P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); - P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); - P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); - P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); - P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); - P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); - P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; - ctx->state[5] += F; - ctx->state[6] += G; - ctx->state[7] += H; -} - -/* - * SHA-256 process buffer - */ -void sha2_update( sha2_context *ctx, const unsigned char *input, int ilen ) -{ - int fill; - unsigned long left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (unsigned long) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, fill ); - sha2_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - sha2_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, ilen ); - } -} - -static const unsigned char sha2_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * SHA-256 final digest - */ -void sha2_finish( sha2_context *ctx, unsigned char output[32] ) -{ - unsigned long last, padn; - unsigned long high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_ULONG_BE( high, msglen, 0 ); - PUT_ULONG_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - sha2_update( ctx, (unsigned char *) sha2_padding, padn ); - sha2_update( ctx, msglen, 8 ); - - PUT_ULONG_BE( ctx->state[0], output, 0 ); - PUT_ULONG_BE( ctx->state[1], output, 4 ); - PUT_ULONG_BE( ctx->state[2], output, 8 ); - PUT_ULONG_BE( ctx->state[3], output, 12 ); - PUT_ULONG_BE( ctx->state[4], output, 16 ); - PUT_ULONG_BE( ctx->state[5], output, 20 ); - PUT_ULONG_BE( ctx->state[6], output, 24 ); - - if( ctx->is224 == 0 ) - PUT_ULONG_BE( ctx->state[7], output, 28 ); -} - -/* - * output = SHA-256( input buffer ) - */ -void sha2( const unsigned char *input, int ilen, - unsigned char output[32], int is224 ) -{ - sha2_context ctx; - - sha2_starts( &ctx, is224 ); - sha2_update( &ctx, input, ilen ); - sha2_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha2_context ) ); -} - -/* - * output = SHA-256( file contents ) - */ -int sha2_file( const char *path, unsigned char output[32], int is224 ) -{ - FILE *f; - size_t n; - sha2_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( 1 ); - - sha2_starts( &ctx, is224 ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - sha2_update( &ctx, buf, (int) n ); - - sha2_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha2_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( 2 ); - } - - fclose( f ); - return( 0 ); -} - -/* - * SHA-256 HMAC context setup - */ -void sha2_hmac_starts( sha2_context *ctx, const unsigned char *key, int keylen, - int is224 ) -{ - int i; - unsigned char sum[32]; - - if( keylen > 64 ) - { - sha2( key, keylen, sum, is224 ); - keylen = ( is224 ) ? 28 : 32; - key = sum; - } - - memset( ctx->ipad, 0x36, 64 ); - memset( ctx->opad, 0x5C, 64 ); - - for( i = 0; i < keylen; i++ ) - { - ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); - ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); - } - - sha2_starts( ctx, is224 ); - sha2_update( ctx, ctx->ipad, 64 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * SHA-256 HMAC process buffer - */ -void sha2_hmac_update( sha2_context *ctx, const unsigned char *input, int ilen ) -{ - sha2_update( ctx, input, ilen ); -} - -/* - * SHA-256 HMAC final digest - */ -void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] ) -{ - int is224, hlen; - unsigned char tmpbuf[32]; - - is224 = ctx->is224; - hlen = ( is224 == 0 ) ? 32 : 28; - - sha2_finish( ctx, tmpbuf ); - sha2_starts( ctx, is224 ); - sha2_update( ctx, ctx->opad, 64 ); - sha2_update( ctx, tmpbuf, hlen ); - sha2_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * SHA-256 HMAC context reset - */ -void sha2_hmac_reset( sha2_context *ctx ) -{ - sha2_starts( ctx, ctx->is224 ); - sha2_update( ctx, ctx->ipad, 64 ); -} - -/* - * output = HMAC-SHA-256( hmac key, input buffer ) - */ -void sha2_hmac( const unsigned char *key, int keylen, - const unsigned char *input, int ilen, - unsigned char output[32], int is224 ) -{ - sha2_context ctx; - - sha2_hmac_starts( &ctx, key, keylen, is224 ); - sha2_hmac_update( &ctx, input, ilen ); - sha2_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha2_context ) ); -} - - -#endif -#endif diff --git a/test/aux-fixed/dkim/sign.pl b/test/aux-fixed/dkim/sign.pl index 6220015ae..a08f38f56 100644 --- a/test/aux-fixed/dkim/sign.pl +++ b/test/aux-fixed/dkim/sign.pl @@ -6,16 +6,18 @@ use Getopt::Long; my $method = "simple/simple"; my $selector = "sel"; my $keyfile = "aux-fixed/dkim/dkim.private"; +my $algorithm = "rsa-sha1"; GetOptions( "method=s" => \$method, "selector=s" => \$selector, "keyfile=s" => \$keyfile, + "algorithm=s" => \$algorithm, ); # create a signer object my $dkim = Mail::DKIM::Signer->new( - Algorithm => "rsa-sha1", + Algorithm => $algorithm, Method => $method, Domain => "test.ex", Selector => $selector, diff --git a/test/confs/4503 b/test/confs/4503 index e9f2d5d47..ddd87d0fa 100644 --- a/test/confs/4503 +++ b/test/confs/4503 @@ -43,5 +43,6 @@ send_to_server: dkim_domain = test.ex dkim_selector = sel dkim_private_key = DIR/aux-fixed/dkim/dkim.private + dkim_sign_headers = From # End diff --git a/test/log/4500 b/test/log/4500 index 4787e6423..0e0f8400d 100644 --- a/test/log/4500 +++ b/test/log/4500 @@ -7,3 +7,6 @@ 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=ses c=simple/simple a=rsa-sha1 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmaY-0005vi-00 signer: test.ex bits: 512 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net +1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha256 b=1024 [verification succeeded] +1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: test.ex bits: 1024 +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net diff --git a/test/log/4503 b/test/log/4503 index 056b52946..39b918a2c 100644 --- a/test/log/4503 +++ b/test/log/4503 @@ -1,4 +1,6 @@ 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 10HmaX-0005vi-00 => a@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]:1225 C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 diff --git a/test/scripts/4500-Domain-Keys-Identified-Mail/4500 b/test/scripts/4500-Domain-Keys-Identified-Mail/4500 index b352893e3..56283992b 100644 --- a/test/scripts/4500-Domain-Keys-Identified-Mail/4500 +++ b/test/scripts/4500-Domain-Keys-Identified-Mail/4500 @@ -4,6 +4,7 @@ exim -DSERVER=server -bd -oX PORT_D **** # # This should pass. +# - sha1, 1024b # Mail original in aux-fixed/4500.msg1.txt # Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple < aux-fixed/4500.msg1.txt client 127.0.0.1 PORT_D @@ -35,6 +36,7 @@ QUIT **** # # This should pass. +# - sha1, 512b # Mail original in aux-fixed/4500.msg1.txt # Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple --selector=ses \ # --keyfile=aux-fixed/dkim/dkim512.private < aux-fixed/4500.msg1.txt @@ -58,6 +60,39 @@ Date: Thu, 19 Nov 2015 17:00:07 -0700 Message-ID: Subject: simple test +This is a simple test. +. +??? 250 +QUIT +??? 221 +**** +# +# This should pass. +# - sha256, 1024b +# Mail original in aux-fixed/4500.msg1.txt +# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=rsa-sha256 \ +# --method=simple/simple < aux-fixed/4500.msg1.txt +client 127.0.0.1 PORT_D +??? 220 +HELO xxx +??? 250 +MAIL FROM: +??? 250 +RCPT TO: +??? 250 +DATA +??? 354 +DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=test.ex; h=from:to + :date:message-id:subject; s=sel; bh=3UbbJTudPxmejzh7U1Zg33U3QT+1 + 6kfV2eOTvMeiEis=; b=xQSD/JMqz0C+xKf0A1NTkPTbkDuDdJbpBuyjjT9iYvyP + Zez+xl0TkoPobFGVa6EN8+ZeYV18zjifhtWYLSsNmPinUtcpKQLG1zxAKmmS0JEh + +qihlWbeGJ5+tK588ugUzXHPj+4JBW0H6kxHvdH0l2SlQE5xs/cdggnx5QX5USY= +From: mrgus@text.ex +To: bakawolf@yahoo.com +Date: Thu, 19 Nov 2015 17:00:07 -0700 +Message-ID: +Subject: simple test + This is a simple test. . ??? 250 diff --git a/test/scripts/4500-Domain-Keys-Identified-Mail/4503 b/test/scripts/4500-Domain-Keys-Identified-Mail/4503 index 7ca338275..cebc62dc3 100644 --- a/test/scripts/4500-Domain-Keys-Identified-Mail/4503 +++ b/test/scripts/4500-Domain-Keys-Identified-Mail/4503 @@ -3,8 +3,10 @@ exim -bd -DSERVER=server -oX PORT_D **** exim a@test.ex +From: nobody@example.com + content **** -millisleep 200 +millisleep 500 killdaemon no_msglog_check -- cgit v1.2.3