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

conn: do not prompt for username with client certificate #3945

Merged
merged 1 commit into from
May 25, 2024

Conversation

balejk
Copy link
Contributor

@balejk balejk commented Jul 28, 2023

  • What does this PR do?

Currently it is not possible to authenticate to a SMTP server using a
client certificate only as neomutt prompts for an username if one is not
provided in the configuration file. This will fail if the server doesn't
support username authentication as seems to be common with servers
expecting a client certificate.

This patch changes the behaviour so that username is only used if it's
explicitly set before the operation is started (e. g. via the
configuration file).

I haven't been able to verify whether it is possible for a server to require
both an username and a client certificate - please let me know if you are - but
this (i. e. not being prompted for the username and having to set it
beforehand) seems to be at least better than not being able to use the client
certificate functionality at all.

Possibly a better solution would be to make cancelling the prompt an option
without interrupting the sending of the message.

  • Does this PR meet the acceptance criteria? (This is just a reminder for you,
    this section can be removed if you fulfill it.)

No.

  • All builds and tests are passing

mutt_path_to_absolute is failing but that doesn't seem to be related.

Updated.

I believe I have not introduced any violation.

  • What are the relevant issue numbers?

Should close #3681.

Cc: @luchr

@balejk balejk requested a review from a team as a code owner July 28, 2023 22:57
@flatcap flatcap added the type:bug Bug label Jul 29, 2023
@balejk
Copy link
Contributor Author

balejk commented Jul 29, 2023

Relevant mutt issue. I shoud have looked at it sooner as it seems their solution is a bit more complicated than what I did and they claim it does what seems as the most reasonable solution to me. I am converting this into a draft until I look into it properly.

@balejk balejk marked this pull request as draft July 29, 2023 09:03
@flatcap
Copy link
Member

flatcap commented Jul 30, 2023

Thanks for pushing ahead with this, @balejk.

Sorry for avoiding the issue.
This is one of those complicated bits of old code.
Nobody really understands them and nobody's brave enough to alter them.

I'll have a read of Mutt's patch and try to understand what they've done and why.

@balejk
Copy link
Contributor Author

balejk commented Jul 30, 2023

No problem, @flatcap.

If I thought to check how mutt handles this before, I wouldn't have opened this until I understood what they did and why.

I intend to see this through, it looks to be a nice excercise, but it might be rather a while until I have time for it, so if you have already started looking into it, feel free to fix it yourself :-)

@flatcap
Copy link
Member

flatcap commented Jul 30, 2023

I haven't started yet -- I'm deep in quite a few other things,

it might be rather a while until I have time for it

There's no rush.

The code's a lot to take in, so ask lots of questions.
Either here on in IRC: #neomutt on irc.libera.chat (web client)

Thanks!

@balejk
Copy link
Contributor Author

balejk commented Jul 30, 2023

Will do, thanks for now!

@vuori
Copy link
Contributor

vuori commented Sep 10, 2023

I can test this if needed since I have an SMTP server that accepts client certs. I haven't used (Neo)mutt's client cert support or looked at the code, so just a few general comments on the matter:

The question of user names/other auth with SMTP (and IMAP) is… complicated. Probably the most common scenario for SMTP is that just having the client cert in the TLS is nego is enough ("implicit client certificate authentication").

Use of the SASL EXTERNAL mechanism is common with IMAP, but rare with SMTP. IIRC neither Thunderbird nor K-9 supports it.

You can have a belt&suspenders configuration where a server requires both client cert and password auth. I would expect this to be quite a bit more uncommon than implicit client cert, but both Thunderbird and K-9 support it. (edit: people who terminate TLS at a proxy and validate the client cert there, but still want user info on the SMTP server are the most likely users for this kind of setup.)

What I think should happen is that if username+password are configured AND the server advertises password mechs in EHLO response, then do the AUTH as usual. If either one is missing, don't prompt and just try to send the mail.

@balejk
Copy link
Contributor Author

balejk commented Sep 10, 2023 via email

@vuori
Copy link
Contributor

vuori commented Sep 10, 2023

@vuori Thank you for the remarks. Sadly, I still haven't found the time to get into this (and I'm afraid it will still be like that for quiet some time), but I will be sure to ping you when there is something to test. Do I understand correctly that you have such a SMTP server to which you have admin access? That could be really useful for testing, although I could probably also try to set it up locally.

Yes, I can issue you a cert and a username/password if/when you need it. Send me a mail when the time is right.

@balejk
Copy link
Contributor Author

balejk commented Sep 10, 2023 via email

@flatcap flatcap force-pushed the client_cert_auth_username branch 2 times, most recently from 2570272 to e9b2fc4 Compare November 16, 2023 19:53
@flatcap flatcap force-pushed the client_cert_auth_username branch 3 times, most recently from 31f9085 to 075ab4d Compare March 29, 2024 01:46
@flatcap flatcap force-pushed the client_cert_auth_username branch from ac4017b to e1b8f9d Compare May 8, 2024 20:10
@balejk balejk force-pushed the client_cert_auth_username branch 2 times, most recently from 27c5b97 to 2a9619e Compare May 18, 2024 11:13
@balejk
Copy link
Contributor Author

balejk commented May 18, 2024

So I have finally started looking into this again. I have gone through the mutt
commits and added a second commit to change the conditions for invoking
smtp_authenticate. The behaviour should now be basically as @vuori suggested,
with the addition of handling of client certificate SASL EXTERNAL.

I have tested this with two of my SMTP providers, one requiring client
certificate and no password AUTH, the other one using just AUTH. With the
former, sending works regardless of whether the username is set (with just
ssl_client_cert, smtp_url, from and optionally smtp_user in neomuttrc).
With the latter (smtp_url, smtp_user, smtp_pass and from in neomuttrc),
sending works too, except for when smtp_user is not set in which case neomutt
doesn't prompt. This is a shame, since smtp_auth_plain does have this
capability but never gets invoked as smtp_authenticate doesn't either because
of the missing username. However it is not a regression -- the current code
also does not call smtp_authenticate with missing username either. In ideal
case we should be able to call smtp_authenticate only when the server
advertises AUTH, however the 91474fdff648 mutt commit (or rather the therein
referenced Debian bug report) claims that sometimes the server can advertise it
even though it is not required.

It remains to test this with a server which requires both the certificate and
AUTH -- @vuori, would you please be able to test this? Or, if you prefer, you
can email me the authentication information for your server to the commit
author email address.

@vuori
Copy link
Contributor

vuori commented May 18, 2024

My usual SMTP server uses implicit client cert auth, but I'll see about setting up a test server that wants both client cert and plain and try it out.

@vuori
Copy link
Contributor

vuori commented May 21, 2024

I didn't have a chance to test the PR itself yet, but I mailed you the keys for a test server that expects client cert + password auth.

@balejk
Copy link
Contributor Author

balejk commented May 21, 2024 via email

@balejk balejk force-pushed the client_cert_auth_username branch from 2a9619e to 9f9368a Compare May 21, 2024 21:43
@balejk balejk marked this pull request as ready for review May 21, 2024 21:44
@balejk
Copy link
Contributor Author

balejk commented May 21, 2024

I have rebased and updated commit messages. If we decide to keep the second
commit, I'd probably want to split out the certificate SASL EXTERNAL enablement
into a separate one for clarity before it's merged.

@vuori
Copy link
Contributor

vuori commented May 22, 2024

Great that you could get your changes tested.

  1. Username + cert auth: I think it's better to not prompt if client cert is set and AUTH is advertised. It's probably a common configuration for servers that support both client cert and password auth to always have AUTH available in EHLO with the cert present, even if it's not required. Exim can be configured to not advertise AUTH if the client cert was accepted, but it's extra work and I don't know whether other SMTP servers support such configuration. Also client certs are a fairly exotic configuration so people who have set one up probably know what they want.

  2. SASL EXTERNAL: IMO spending time on this is probably not super useful, but it could be nice to have. I can make the test server support it if needed. Let me know if you need it and whether you want it separately (= keep the current setup intact on the current port and have only SASL EXTERNAL available when connecting through another port) or just make it available in addition to PLAIN.

  3. Forced auth prompt if server advertises AUTH: I think at minimum implementing one of the bypass mechanisms is a must. Probably nowadays a majority of servers require some kind of password/cert auth to send, but if a server supports both password-authenticated and traditional IP-authenticated sending, it will most likely always be advertising AUTH in EHLO. Requiring credentials with no opt-out will probably annoy some group of users. However since that group is likely not very large these days, having the prompting happen as default could be ok; I'll defer to flatcap et al. on this.

@flatcap
Copy link
Member

flatcap commented May 22, 2024

Thanks for your work on this!
The code changes look fine to me.

As for the second commit, I don't know.
You have a much better understanding of whether it's necessary or not.

Upstream do have the commit, so I'd be inclined to merge it,
but I don't understand the problems it might cause / solve.

Remove the username population code so that it's only used if it has
been set explicitly. Without this, it is not possible to use a SMTP
server expecting implicit client certificate authentication as the
username will trigger SMTP authentication which will fail if the server
doesn't support it as is common in this setup.

Upstream commit: 191b0513b43d5e603f99292faa5f8ebcc1be3823
@balejk balejk force-pushed the client_cert_auth_username branch from 9f9368a to 3bb880f Compare May 22, 2024 13:47
@balejk
Copy link
Contributor Author

balejk commented May 22, 2024 via email

@vuori
Copy link
Contributor

vuori commented May 22, 2024

IIRC the parameter to SASL EXTERNAL is mostly useless with client certs. In the rare case that EXTERNAL is used with SMTP, I expect doing anything with the parameter on the server would be even rarer because it's just an untrusted string from the client; everything you want to know should be in the certificate anyway (maybe it's useful for proxy auth scenarios for specifying the user you want to proxy to? this would be more of an IMAP thing though). In other words, sending an empty string as the parameter to EXTERNAL should be ok most of the time. Sending the username is probably fine too.

I'll see if setting up EXTERNAL is simple and get back to you in mail about that. I don't think we should hold up this PR with related concerns though.

@balejk
Copy link
Contributor Author

balejk commented May 22, 2024 via email

Copy link
Member

@flatcap flatcap left a comment

Choose a reason for hiding this comment

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

Thanks for the recap.
Great work!

@flatcap flatcap merged commit 1949072 into neomutt:main May 25, 2024
4 checks passed
@balejk
Copy link
Contributor Author

balejk commented May 25, 2024 via email

@balejk
Copy link
Contributor Author

balejk commented May 28, 2024

So I have had the opportunity to test the behaviour with a server which supports certitificate-based SASL EXTERNAL (again graciously set up by @vuori) and it seems the situation is as follows.

With the current main (so the the merge of this PR), this type of authentication works with no problems if the server wants a username in addition. If the username is not set in neomutt, smtp_authenticate is never invoked despite the certificate being available and sending fails because the sender is not authenticated (so this only concerns the case when the authentication is required by the server).

The second commit which I had here helps with that, triggering the authentication in case the username is missing but cert is set (at the same time, it does not try to authenticate if the server does not advertise it as otherwise it would break implicit client cert auth again). Here is the patch for convenience:

diff --git a/send/smtp.c b/send/smtp.c
index b09fad17c493..145ca2c0ef08 100644
--- a/send/smtp.c
+++ b/send/smtp.c
@@ -1071,16 +1071,14 @@ static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
   }
 #endif
 
-  if (force_auth || adata->conn->account.flags & MUTT_ACCT_USER)
-  {
-    if (!(adata->capabilities & SMTP_CAP_AUTH))
-    {
-      mutt_error(_("SMTP server does not support authentication"));
-      return -1;
-    }
-
+  if (adata->capabilities & SMTP_CAP_AUTH &&
+      (adata->conn->account.flags & MUTT_ACCT_USER ||
+       force_auth
+#ifdef USE_SSL
+       || cs_subset_path(NeoMutt->sub, "ssl_client_cert")
+#endif
+      ))
     return smtp_authenticate(adata);
-  }
 
   return 0;
 }

However, in this case neomutt still prompts for the username and if the prompt is left empty it sends the empty string. This should be no problem with Exim which apparently either ignores the username altogether or can expect a specific one(s) (so smtp_user will have a valid value and it will work even without the above patch -- note however that setting smtp_user to an empty string seems to be the same as if it was not set at all, so in such case the patch is still needed). However it's not clear whether other server implementations behave the same and thus whether it could be an issue that it doesn't seem possible to prevent neomutt from sending any username at all (while also having smtp_authenticate run). So it might turn out to be most robust to just remove all the prompts and rely only on what the user has put in neomuttrc (or :set at runtime) as we probably usually expect the user to know the necessary configuration, especially with such an exotic setup.

I hope this information may prove useful to someone in the future but I leave further investigation and possible neomutt patches to them :-)

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

Successfully merging this pull request may close these issues.

tls_get_client_cert triggers user-password authentication even if not wanted
3 participants