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

Support for Capability items to be 'dynamic' #86

Closed

Conversation

shaun-a-johnson
Copy link
Contributor

@shaun-a-johnson shaun-a-johnson commented Jun 21, 2018

This patch converts the previous string-literal representation of the capabilities to a dynamic list of 'capability objects' each reflecting the capability word they intend to add along with flags that represent when the capability word is to be displayed (eg: varying states).

Upon a request for the capability string the list of capability objects is iterated and a capability string is dynamically built by appending each capability word if the conditions described in the flags of the capability object are met.

This patch provides full backwards compatibility for existing plugins that might have previously used client_add_capability as well as honors (and converts) existing string literal capability words into equivalent additions to the new list of capability objects.

Some examples of circumstances that can be specified when adding
new capabilities are:

- Only show at pre-login or post-login
- Only show if any TLS/SSL is active/inactive
- Only show if plaintext auth login is enabled

The default operation is to show the capabaility word if at least
one of the circumstances is met, an additional flag provides the
ability to only show the capability word if exactly all of the
circumstances are met.

The API for adding capabilities now simply wraps the API for adding
entries to the capability list with the 'show always' flag set.

Retrieving the capability string is now a loop that iterates the list
of capability objects and evaluates whether each capability should be
added to the capability string based on the state of the current IMAP
session.

@jeffpc
Copy link
Contributor

jeffpc commented Jun 24, 2018

The commit history of the PR needs a bit of cleanups as well. E.g.,

  • the author shouldn't be 'root'
  • the merge commit shouldn't exist

Copy link
Contributor

@jeffpc jeffpc left a comment

Choose a reason for hiding this comment

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

The commit message should say why LITERAL+/- got moved STARTTLS. I haven't looked at other commit messages in detail, but they should explain why it does things if the change is not trivially explained by the one-liner.

In general, we try to make sure that every commit is good. So, the commit that readds client_add_capability makes me believe that somewhere along this series things were broken.

These aren't too difficult to fix up with some judicious use of git rebase -i.

@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 2 times, most recently from 89312a1 to 5f78d31 Compare June 26, 2018 18:46
@shaun-a-johnson
Copy link
Contributor Author

shaun-a-johnson commented Jun 26, 2018

I have tended to all of the above comments and adjusted the commit history with git rebase.
Please consider this change for merge with the upstream source, and please let me know if there are any further issues or changes that can be applied.

@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 3 times, most recently from 55ff4ac to da10775 Compare June 26, 2018 19:29
Copy link
Contributor

@sirainen sirainen left a comment

Choose a reason for hiding this comment

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

Also in general this should be split to a couple of different patches. Perhaps:

  • Add imap_capability_list_*() API
  • Use the API in imap-login
  • Use the API in imap minimally
  • It looks like client_add_capability() keeps working, so the old API still works. So, change the existing client_add_capability() calls in a separate last commit to the new API.

@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 3 times, most recently from a3eee3f to c8c3d7d Compare August 10, 2018 19:13
@shaun-a-johnson
Copy link
Contributor Author

shaun-a-johnson commented Aug 10, 2018

Thank you for reviewing this work Timo, I have tended to all of your suggestions and I have divided the changeset into four logical commits.

If anybody could review this changeset again now that I have pushed the updated branch with all the changes it would be greatly appreciated.

Also, I had a question which I posted in response to your suggestion to split out the function imap_capability_list_get_capabilities regarding the new for-loop in that new function. If you wouldn't mind reviewing my question I feel there is room for some improvement but I am not sure what the best approach would be.

@shaun-a-johnson
Copy link
Contributor Author

Was wondering if there were any additional changes required for this request?
I fully realize it's a pretty large changeset - if it would be easier to break it down into smaller chunks please let me know and I'd be happy to accomodate.

@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 3 times, most recently from 966d365 to 0b3773e Compare December 20, 2018 02:01
@shaun-a-johnson
Copy link
Contributor Author

Greetings again dovecot team,

I know you're all busy with a recent release but I wanted to check in again to see if there is anything that I can do to help expedite this pull request? Still uncertain if the recent changes made satisfy the changes requested by Timo or if additional updates are still required?

Thank you in advance!

Copy link
Contributor

@sirainen sirainen left a comment

Choose a reason for hiding this comment

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

Mostly coding style consistency issues, but also found some bugs.

/* add STARTTLS to cap list if it is needed */
if (strcmp(client->ssl_set->ssl, "no") != 0) {
imap_capability_list_add(imap_client->capability_list,
"STARTTLS", IMAP_CAP_VISIBILITY_REQUIRE_ALL |
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this now returns STARTTLS when connecting to imaps port. Also should likely use client_is_tls_enabled(client) instead of strcmp(client->ssl_set->ssl, "no") != 0

Copy link
Contributor Author

@shaun-a-johnson shaun-a-johnson Jul 11, 2019

Choose a reason for hiding this comment

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

I think this now returns STARTTLS when connecting to imaps port.

Is there any changes I need to do regarding this?

Other than that, I have updated the strcmp to client_is_tls_enabled().

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually it seems to be a bit wrong. Anyway my main concern was that the original code was:

	if (client_is_tls_enabled(client) && !client->tls)
		str_append(cap_str, " STARTTLS");

But your code isn't using client->tls anywhere. It seems you're actually using client->starttls. So I think now if you connect to localhost the CAPABILITY list won't show STARTTLS?

Copy link
Contributor Author

@shaun-a-johnson shaun-a-johnson Jul 11, 2019

Choose a reason for hiding this comment

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

Hmmm... I think I understand what you're saying...

The STARTTLS capability is being registered with:

               imap_capability_list_add(imap_client->capability_list, "STARTTLS",
                                        IMAP_CAP_VISIBILITY_FLAG_REQUIRE_ALL |
                                        IMAP_CAP_VISIBILITY_PREAUTH |
                                        IMAP_CAP_VISIBILITY_INSECURE);

This means that STARTTLS will ONLY be shown if the connection is INSECURE (not localhost, no tls, etc), and we actually want to display STARTTLS over secured non-tls/ssl connections like localhost, right?

So would it work if we changed the registration to (TLS_INACTIVE instead of INSECURE):

               imap_capability_list_add(imap_client->capability_list, "STARTTLS",
                                        IMAP_CAP_VISIBILITY_FLAG_REQUIRE_ALL |
                                        IMAP_CAP_VISIBILITY_PREAUTH |
                                        IMAP_CAP_VISIBILITY_TLS_INACTIVE);

That way in the get_capability function we will have:

    /* is tls/ssl enabled? */
    if (client->tls)
            flags |= IMAP_CAP_VISIBILITY_TLS_ACTIVE;
    else
            flags |= IMAP_CAP_VISIBILITY_TLS_INACTIVE;

The above will effectively perform the !client->tls check from the original code, and then the other half of the check is performed at the time of registering the STARTTLS keyword:

    if (client_is_tls_enabled(client)) { 
            imap_capability_list_add(imap_client->capability_list, "STARTTLS",
                                     IMAP_CAP_VISIBILITY_FLAG_REQUIRE_ALL |
                                     IMAP_CAP_VISIBILITY_PREAUTH |
                                     IMAP_CAP_VISIBILITY_TLS_INACTIVE);
    }

So as long as TLS is enabled we will add STARTTLS to the capability list under the condition that it is only ever shown at: preauth when TLS/SSL is not active whatsoever.

Is this an accurate solution to the problem you are describing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just wanted to follow up - haven't seen any response on the proposed solution - can you please let me know if what has been proposed is an accurate solution?

Thanks!

@shaun-a-johnson
Copy link
Contributor Author

shaun-a-johnson commented Jul 11, 2019

Thanks for taking the time to review this Timo, I have pushed another branch which I believe should solve all of the points you have listed.

There is one outstanding question above regarding the STARTTLS capability, I believe I have solved the issue you described but I will leave that issue open for further discussion on that topic.

I will now look to generating automated tests for this, I will post an update if I run into any issues.

Edit: A few things seemed to be an oversight on my part, the include for "lib.h" in imap-capability-list.h is necessary for the use of the BIT(n) macros.

@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 2 times, most recently from 323714d to 0f547c9 Compare July 12, 2019 18:57
@shaun-a-johnson shaun-a-johnson force-pushed the dynamic-capabilities branch 4 times, most recently from 6929656 to e8e1221 Compare July 19, 2019 18:28
@shaun-a-johnson
Copy link
Contributor Author

capabilities_tests.tar.gz

I've attached to this post a tarball containing a very simple bash script and a series of imap test files that can be used in conjunction with the imaptest program.

The bash script will modify the dovecot configuration to cause changes to the capability, reload dovecot, and then run the respective imaptest unit file for the difference in capability.

I may have missed some, but what I gathered was these are all the settings I can manipulate to trigger capability banner changes:

    - ssl = no

            This causes STARTTLS to no longer appear at pre-login

    - imap_literal_minus = yes
    
            This results in LITERAL- instead of LITERAL+

    - mailbox_list_index = no

            This results in the NOTIFY capability disappearing

    - imap_urlauth_host = asdf
      mail_attribute_dict = asdf

            These two cause URLAUTH and URLAUTH=BINARY to appear

    - imap_metadata = 1
    
            This causes METADATA to appear
     
    - *commented out all special_use mailboxes in 15-mailboxes.conf*

            This causes SPECIAL-USE to disappear

    - auth_mechanisms = login

            This causes LOGINDISABLED to appear when connecting over localhost.

The result of running the test script against the upstream master should give the same output as running the test script against this patch.

The expected output is:

1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/4 individual commands failed
extensions: 0/0 individual commands failed
1 test groups: 0 failed, 0 skipped due to missing capabilities
base protocol: 0/2 individual commands failed
extensions: 0/0 individual commands failed

Hopefully this should help to expedite the testing process for this patch, if there was anything I've missed or handled incorrectly here then please let me know.

@cmouse cmouse closed this Nov 26, 2019
@sirainen sirainen reopened this Feb 11, 2020
@sirainen
Copy link
Contributor

Waiting for extra time / clear use case..

@kmcgrail
Copy link

kmcgrail commented May 4, 2020

I like the idea of this. Seems necessary to move dovecot to more of a plugin-based concept that if capabilities are added/removed by a plugin, that has to be surfaced in the plugin chain.

Added a new API to lib-imap which exposes functions for manipulating a
capability list.  This addition is the first step towards conditionally
displaying capabilities.  For example, displaying certain capabilities
to the client only after TLS has been negotiated.
Updated imap-login to use the new capability list API from lib-imap
which will allow for pre-login IMAP capabilities to be displayed
conditionally.
This adds two new boolean fields: 'secured', and 'ssl_secured' to the
imap client structure, these reflect the values of conn_secured and
conn_ssl_secured respectively.

This is a prerequisite to integrating the imap_capability_list APIs into
imap because imap needs to distinguish between secured/tls/non-secured
connections when determining whether conditional capabilities should be
displayed or not.
Updated imap to use the new capability list API from lib-imap which will
allow for post-login imap capabilities to be displayed conditionally.
This ensures functionality of client_add_capability() does not change,
and it introduces a new imap function client_get_capability() which is
now used everywhere that client->capability_string was previously used.
@nerdlem
Copy link

nerdlem commented Jan 5, 2022

Just wanted to add a comment in support of this feature. Plugins that can cause the capabilities they provide dynamically seems like a great idea.

@DovecotLtd DovecotLtd deleted the branch dovecot:master February 24, 2022 07:36
@DovecotLtd DovecotLtd closed this Feb 24, 2022
@cmouse
Copy link
Contributor

cmouse commented Feb 24, 2022

Please reopen against main.

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.

7 participants