From c23a059aab7a84c8e4bc876bf7171dc0511bba99 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 8 Aug 2023 23:35:43 +0200 Subject: [PATCH] oxdisco: more detailed secondary store access validation If the entry from the MySQL scndstore_hints table is missing, the request would be inadvertently rejected even if opening the mailbox did succeed. --- doc/autodiscover.4gx | 2 +- exch/oxdisco.cpp | 51 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/doc/autodiscover.4gx b/doc/autodiscover.4gx index e90ffec58..742b919ec 100644 --- a/doc/autodiscover.4gx +++ b/doc/autodiscover.4gx @@ -70,7 +70,7 @@ When OL opens a non-default store (store of another user) or a public store, it may also make an AutoDiscover inquiry for the extra store. This setting controls whether the server-side AutoDiscover module should perform a permission check on non-default stores and possibly reject returning connection -details. (Inquiry Public stores are always permitted.) +details. (Inquiry of public stores are always permitted.) .br Default: \fIyes\fP .TP diff --git a/exch/oxdisco.cpp b/exch/oxdisco.cpp index 653134843..dcc6bbaef 100644 --- a/exch/oxdisco.cpp +++ b/exch/oxdisco.cpp @@ -19,6 +19,7 @@ #include #include #include +#include /* MESSAGE_CONTENT alias */ #include #include #include @@ -51,8 +52,18 @@ class OxdiscoPlugin { decltype(mysql_adaptor_get_domain_ids) *get_domain_ids; decltype(mysql_adaptor_scndstore_hints) *scndstore_hints; decltype(mysql_adaptor_get_homeserver) *get_homeserver; + decltype(mysql_adaptor_meta) *meta; } mysql; // mysql adaptor function pointers + struct _exmdb { + _exmdb(); + #define EXMIDL(n, p) EXMIDL_RETTYPE (*n) p; + #define IDLOUT + #include + #undef EXMIDL + #undef IDLOUT + } exmdb; + private: std::string x500_org_name = "Gromox default"; uint server_id; // Hash of the name of the mail server @@ -103,7 +114,7 @@ static constexpr char server_base_dn[] = "/o={}/ou=Exchange Administrative Group " "(FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn={}@{}", public_folder[] = "Public Folder", - public_folder_email[] = "public.folder.root@", + public_folder_email[] = "public.folder.root@", /* EXC: PUBS@thedomain */ bad_address_code[] = "501", bad_address_msg[] = "Bad Address", invalid_request_code[] = "600", @@ -244,6 +255,11 @@ static std::string extract_qparam(const char *qstr, const char *srkey) return ret; } +/** + * Is it ok to give out metadata, such as their PR_DISPLAY_NAME? + * If @actor has _any_ kind of permission (or a scndstore hint entry), + * then allow it. + */ BOOL OxdiscoPlugin::access_ok(int ctx_id, const char *target, const char *actor) { if (m_validate_scndrequest == 0 || strcasecmp(target, actor) == 0 || @@ -260,7 +276,20 @@ BOOL OxdiscoPlugin::access_ok(int ctx_id, const char *target, const char *actor) if (std::any_of(hints.begin(), hints.end(), [&](const sql_user &u) { return strcasecmp(u.username.c_str(), target) == 0; })) return true; - /* authuser has no scndstore matching email */ + sql_meta_result mres; + err = mysql.meta(target, WANTPRIV_METAONLY, mres); + if (err != 0) { + mlog(LV_ERR, "oxdisco: cannot retrieve usermeta for %s", strerror(err)); + return die(ctx_id, server_error_code, server_error_msg); + } + uint32_t perm = 0; + if (!exmdb.get_mbox_perm(mres.maildir.c_str(), actor, &perm)) { + mlog(LV_ERR, "oxdisco: cannot access mailbox of %s to test for permissions", target); + return die(ctx_id, server_error_code, server_error_msg); + } + fprintf(stderr, "oxdisco: perm=%u\n", perm); + if (perm != 0) + return true; return die(ctx_id, bad_address_code, (bad_address_msg + " (403 Permission Denied)"s).c_str()); } @@ -1067,15 +1096,31 @@ OxdiscoPlugin::_mysql::_mysql() { #define getService(f) \ if (query_service2(# f, f) == nullptr) \ - throw std::runtime_error("[ews]: failed to get the \""# f"\" service") + throw std::runtime_error("oxdisco: failed to get the \""# f"\" service") getService(get_user_displayname); getService(get_user_ids); getService(get_domain_ids); getService(scndstore_hints); getService(get_homeserver); + query_service2("mysql_auth_meta", meta); + if (meta == nullptr) + throw std::runtime_error("oxdisco: failed to get the \"meta\" symbol"); #undef getService } +OxdiscoPlugin::_exmdb::_exmdb() +{ +#define EXMIDL(n, p) do { \ + query_service2("exmdb_client_" #n, n); \ + if ((n) == nullptr) \ + throw std::runtime_error("oxdisco: failed to get the \"exmdb_client_"# n"\" service\n"); \ +} while (false); +#define IDLOUT +#include +#undef EXMIDL +#undef IDLOUT +} + /////////////////////////////////////////////////////////////////////////////// //Plugin management