Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPCC-32678 Add support for TLS communication to Std.System.Email.SendEmail() #19128

Merged
merged 1 commit into from
Sep 26, 2024

Conversation

dcamper
Copy link
Contributor

@dcamper dcamper commented Sep 18, 2024

Type of change:

  • This change is a bug fix (non-breaking change which fixes an issue).
  • This change is a new feature (non-breaking change which adds functionality).
  • This change improves the code (refactor or other change that does not change the functionality)
  • This change fixes warnings (the fix does not alter the functionality or the generated code)
  • This change is a breaking change (fix or feature that will cause existing behavior to change).
  • This change alters the query API (existing queries will have to be recompiled)

Checklist:

  • My code follows the code style of this project.
    • My code does not create any new warnings from compiler, build system, or lint.
  • The commit message is properly formatted and free of typos.
    • The commit message title makes sense in a changelog, by itself.
    • The commit is signed.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly, or...
    • I have created a JIRA ticket to update the documentation.
    • Any new interfaces or exported functions are appropriately commented.
  • I have read the CONTRIBUTORS document.
  • The change has been fully tested:
    • I have added tests to cover my changes.
    • All new and existing tests passed.
    • I have checked that this change does not introduce memory leaks.
    • I have used Valgrind or similar tools to check for potential issues.
  • I have given due consideration to all of the following potential concerns:
    • Scalability
    • Performance
    • Security
    • Thread-safety
    • Cloud-compatibility
    • Premature optimization
    • Existing deployed queries will not be broken
    • This change fixes the problem, not just the symptom
    • The target branch of this pull request is appropriate for such a change.
  • There are no similar instances of the same problem that should be addressed
    • I have addressed them here
    • I have raised JIRA issues to address them separately
  • This is a user interface / front-end modification
    • I have tested my changes in multiple modern browsers
    • The component(s) render as expected

Smoketest:

  • Send notifications about my Pull Request position in Smoketest queue.
  • Test my draft Pull Request.

Testing:

Testing performed by using a local SMTP dev tool (https://github.com/rnwood/smtp4dev) and monitoring the ESMTP conversation with various configurations.

@dcamper dcamper requested a review from mckellyln September 18, 2024 21:30
Copy link

Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-32678

Jirabot Action Result:
Workflow Transition To: Merge Pending
Updated PR

@@ -472,8 +472,10 @@ class CMailInfo
unsigned port;
StringAttr sender;
Owned<ISocket> socket;
Owned<ISecureSocket> secureSocket;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. That was part of the test change and If forgot to remove it.

char* buffPtr = inbuff;
while (buffPtr < (inbuff + inlen))
{
if (strncmp(buffPtr, "250", 3) == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the "250" occur not at the beginning of buffPtr ?
And if in this block, could buffPtr go past inbuff + inlen ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that there will always be at least one 250 response that serves as an acknowledgement of the EHLO. In my tests, that line was "250-Nice to meet you." but the exact wording is not dictated.

Response codes will always be at the beginning of the buffer, and 250 responses should be first if there is no error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the size of the outer while() to abort earlier to avoid some scans outside of the buffer.

@dcamper dcamper requested a review from mckellyln September 19, 2024 13:49
Copy link
Contributor

@mckellyln mckellyln left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, approved.

@dcamper dcamper force-pushed the hpcc-32678-smtp-tls branch from da4f77d to 8e45a02 Compare September 19, 2024 17:58
@dcamper
Copy link
Contributor Author

dcamper commented Sep 19, 2024

Thanks, Mark! @ghalliday Squashed and ready for merging.

Copy link
Member

@ghalliday ghalliday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcamper one issue that probably should be fixed, and a couple of questions.

@@ -505,6 +506,20 @@ class CMailInfo
validator.validateValue(subject.get(), "email subject");
}

void convertToTLSSocket()
{
secureSocketContext.setown(createSecureSocketContext(ClientSocket));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but does this need the ability to provide a client certificate for mtls? If so this will need enhanced configuration support (from a secret).


#if defined(_USE_OPENSSL)
bool useTLS = false;
if (inlen > 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (inlen > 12), otherwise a very short response could access invalid memory

Copy link
Contributor Author

@dcamper dcamper Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The while condition a couple of lines down guards against an invalid access. Aborting the loop earlier is slightly more efficient, though.

bool useTLS = false;
if (inlen > 0)
{
// Walk the buffer, looking for the STARTTLS option
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth commenting that 250 responses must come first. Given the comment my expectation was that this would keep skipping to the next line through the buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

250 codes should be the only non-error responses at this point, but the options that are returned can be in any order. So really, this is a line-by-line scan, aborting as soon as we can process '250 STARTTLS' or '250-STARTTLS'.

void getEhloAndOptions(StringBuffer & out)
{
// Initial EHLO and gather reply, which should contain ESMTP options
getEhlo(out);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to fall back to HELO if EHLO not supported? Can we ignore for any modern mail server?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESMTP came out in 1995 (RFC 1869). All mail servers should support ESMTP by now. That said, a misconfiguration on the server side could prompt a fallback to the original protocol. I will add that.

@dcamper
Copy link
Contributor Author

dcamper commented Sep 20, 2024

@ghalliday I incorporated your feedback in this latest commit. It included a slight refactoring to make the flow more sensible. Please give it another look.

@dcamper dcamper requested a review from ghalliday September 20, 2024 14:00
@dcamper
Copy link
Contributor Author

dcamper commented Sep 20, 2024

Note additional change: After reviewiing the RFC I discovered that the option name (STARTTLS) should be tested case-insensitive. I made that change to the code as well.

Copy link
Member

@ghalliday ghalliday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcamper looks good. One trivial change. Please squash.

// Log error as a warning
StringBuffer msg;
info.addToWarnings(e->errorMessage(msg).str());
EXCLOG(MCoperatorError, e, "WARNING");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCoperatorWarning?

Copy link
Contributor Author

@dcamper dcamper Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied that from a catch() a little further down in the code. Do you have a better way of doing this?

@dcamper dcamper force-pushed the hpcc-32678-smtp-tls branch from a720b2d to 2c48403 Compare September 23, 2024 13:21
@dcamper
Copy link
Contributor Author

dcamper commented Sep 23, 2024

@ghalliday Squashed. Thanks!

@ghalliday
Copy link
Member

@dcamper one final thought. What is the best version to target? Could this possibly break existing code?

@dcamper
Copy link
Contributor Author

dcamper commented Sep 25, 2024

Any change introduces a possibility for things to go wrong. The most likely failure here lies with an errant external mail server. The possibilities include:

  • Server does not support ESMTP. The various RFCs state that the server should return a 502 code after we send EHLO. This would be detected as an error and an exception thrown. In this PR's modifications, the initial EHLO is wrapped in a try/catch for this eventuality, and the code reverts to sending HELO instead (the old behavior). This is an unlikely scenario, as ESMPT was introduced 29 years ago; every mail server that is currently running should support ESMTP.
  • ESMTP is supported but TLS is not. In this case the server will not reply with STARTTLS. This PR's modifications make enabling TLS optional, and will upgrade the connection only if the mail server indicates support.
  • Misconfigured server. Server detects that client is enabling ESMTP and in the course of activating ESMTP extensions, something goes wrong. This may cause the mail server to not complete the protocol. This would occur within our try/catch and then cause a fallback to SMTP, which should work because we presumably have been communicating with this server in the past, using plain SMTP.

The possibility of a introducing an outright "failure to communicate" is very small, I think. All of that said, we can certainly retarget to 9.8 or even master (9.10) in an abundance of caution.

@dcamper
Copy link
Contributor Author

dcamper commented Sep 25, 2024

Final thought: If this PR is not safe for 9.6.x then I doubt it would be safe for 9.8.x, either. So the choices will be either 9.6.x or master.

@ghalliday ghalliday changed the base branch from candidate-9.6.x to candidate-9.8.x September 26, 2024 11:34
@ghalliday ghalliday merged commit 2484ceb into hpcc-systems:candidate-9.8.x Sep 26, 2024
49 checks passed
@ghalliday
Copy link
Member

Merged to 9.8.x for the moment because most production systems are not yet upgraded to that version. So I agree with your assesment of risk, a problem will not have the same potential impact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants