processone / ejabberd

Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server)
https://www.process-one.net/en/ejabberd/
Other
6k stars 1.5k forks source link

PEP notification filtering entity capabilities ignored #4158

Open dansebcar opened 5 months ago

dansebcar commented 5 months ago

Environment

Bug description

In the below C program, I publish some text to the http://jabber.org/protocol/activity PEP node every second. After each publish, I receive a <message type="headline" /> with a copy of the content (because I am implicitly subscribed to my own PEP node). I would not like to receive those.

Following XEP 0115, I declare my entity capabilities with the verification string iaTkyKUZtplRPLviKWlgoSGj+Do= corresponding to no capabilities (it's the base64 SHA-1 of client/bot//<). Ejabberd then asks for my capabilities, and I respond with <identity type="bot" category="client"/>, no <feature var='http://jabber.org/protocol/activity+notify'/> as in XEP 0060. But I still receive the notifications. This contradicts:

The server MUST NOT send notifications related to any NodeIDs that the contact's client has not asked for via the relevant "NodeID+notify" disco#info feature.

(Also, I notice that the disco#info is only sent from the server to the client once per SHA-1 hash. I tried setting mod_caps: { use_cache: false } on the server but it didn't ask again. It only asks again if I run sudo ejabberdctl clear_cache on the server. Should I open a separate issue about that?)

Here are the libstrophe debug logs:

xmpp DEBUG Connecting via altdomain.
xmpp DEBUG sock_connect() to livedata.ammonit.com:443 returned 3
xmpp DEBUG Attempting to connect to livedata.ammonit.com
xmpp DEBUG connection successful
xmpp DEBUG using legacy SSL connection
tls DEBUG Certificate verification passed
tls DEBUG Subject=/CN=livedata.ammonit.com
tls DEBUG Issuer=/C=US/O=Let's Encrypt/CN=R3
conn DEBUG SENT: <?xml version="1.0"?><stream:stream to="livedata.ammonit.com" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" from="dc@livedata.ammonit.com">
xmpp DEBUG RECV: <stream:stream id="3341328037043975360" version="1.0" lang="en" to="dc@livedata.ammonit.com" from="livedata.ammonit.com">
xmpp DEBUG RECV: <features xmlns="http://etherx.jabber.org/streams"><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>EXTERNAL</mechanism><mechanism>PLAIN</mechanism><mechanism>SCRAM-SHA-1-PLUS</mechanism><mechanism>SCRAM-SHA-1</mechanism></mechanisms></features>
conn DEBUG SENT: <auth mechanism="SCRAM-SHA-1" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">[redacted]</auth>
xmpp DEBUG RECV: <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">[redacted]</challenge>
xmpp DEBUG handle SCRAM-SHA-1 (challenge) called for challenge
conn DEBUG SENT: <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">[redacted]</response>
xmpp DEBUG RECV: <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">[redacted]</success>
xmpp DEBUG handle SCRAM-SHA-1 (challenge) called for success
xmpp DEBUG SASL SCRAM-SHA-1 auth successful
conn DEBUG SENT: <?xml version="1.0"?><stream:stream to="livedata.ammonit.com" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" from="dc@livedata.ammonit.com">
xmpp DEBUG RECV: <stream:stream id="5654202522392670665" version="1.0" lang="en" to="dc@livedata.ammonit.com" from="livedata.ammonit.com">
xmpp DEBUG Reopened stream successfully.
xmpp DEBUG RECV: <features xmlns="http://etherx.jabber.org/streams"><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"><optional/></session><c hash="sha-1" xmlns="http://jabber.org/protocol/caps" node="http://www.process-one.net/en/ejabberd/" ver="7NTKFlsHRSbtyvLJBBwdXv3A0AQ="/><sm xmlns="urn:xmpp:sm:2"/><sm xmlns="urn:xmpp:sm:3"/><ver xmlns="urn:xmpp:features:rosterver"/><csi xmlns="urn:xmpp:csi:0"/></features>
conn DEBUG SENT: <iq id="_xmpp_bind1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>caps-test</resource></bind></iq>
xmpp DEBUG RECV: <iq id="_xmpp_bind1" type="result"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>dc@livedata.ammonit.com/caps-test</jid></bind></iq>
xmpp DEBUG Bind successful.
conn DEBUG SENT: <enable xmlns="urn:xmpp:sm:3" resume="true"/>
conn DEBUG SENT: <presence><c hash="sha-1" xmlns="http://jabber.org/protocol/caps" ver="iaTkyKUZtplRPLviKWlgoSGj+Do=" node="http://example.com/caps-test"/></presence>
xmpp DEBUG RECV: <enabled id="g2gCbQAAAAljYXBzLXRlc3RtAAAACMwmnDVzcAeg" xmlns="urn:xmpp:sm:3" max="300" resume="true"/>
xmpp DEBUG RECV: <iq id="rr-1706873809542-7958309740174706942-1nPT6CW72MRL/kghGosU6VXSxNc=-55238004" type="get" to="dc@livedata.ammonit.com/caps-test" from="dc@livedata.ammonit.com/caps-test"><query xmlns="http://jabber.org/protocol/disco#info" node="http://example.com/caps-test#iaTkyKUZtplRPLviKWlgoSGj+Do="/></iq>
conn DEBUG SENT: <iq id="rr-1706873809542-7958309740174706942-1nPT6CW72MRL/kghGosU6VXSxNc=-55238004" to="dc@livedata.ammonit.com/caps-test" type="result"><query xmlns="http://jabber.org/protocol/disco#info" node="http://example.com/caps-test#iaTkyKUZtplRPLviKWlgoSGj+Do="><identity type="bot" category="client"/></query></iq>
conn DEBUG SENT: <r xmlns='urn:xmpp:sm:3'/>
xmpp DEBUG RECV: <r xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <a h="1" xmlns="urn:xmpp:sm:3"/>
xmpp DEBUG RECV: <presence to="dc@livedata.ammonit.com/caps-test" lang="en" from="dc@livedata.ammonit.com/caps-test"><c hash="sha-1" xmlns="http://jabber.org/protocol/caps" node="http://example.com/caps-test" ver="iaTkyKUZtplRPLviKWlgoSGj+Do="/><x xmlns="vcard-temp:x:update"/></presence>
xmpp DEBUG RECV: <a h="2" xmlns="urn:xmpp:sm:3"/>
xmpp DEBUG RECV: <r xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <a h="2" xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <iq id="current" type="set"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="http://jabber.org/protocol/activity"><item><text>Hello, World!</text></item></publish></pubsub></iq>
conn DEBUG SENT: <r xmlns='urn:xmpp:sm:3'/>
xmpp DEBUG RECV: <message type="headline" to="dc@livedata.ammonit.com" from="dc@livedata.ammonit.com"><event xmlns="http://jabber.org/protocol/pubsub#event"><items node="http://jabber.org/protocol/activity"><item id="6AAD5532757F5"><text>Hello, World!</text></item></items></event><addresses xmlns="http://jabber.org/protocol/address"><address jid="dc@livedata.ammonit.com/caps-test" type="replyto"/></addresses></message>
xmpp DEBUG RECV: <r xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <a h="3" xmlns="urn:xmpp:sm:3"/>
xmpp DEBUG RECV: <iq id="current" type="result" to="dc@livedata.ammonit.com/caps-test" lang="en" from="dc@livedata.ammonit.com"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="http://jabber.org/protocol/activity"><item id="6AAD5532757F5"/></publish></pubsub></iq>
xmpp DEBUG RECV: <a h="3" xmlns="urn:xmpp:sm:3"/>
xmpp DEBUG RECV: <r xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <a h="4" xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <iq id="current" type="set"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="http://jabber.org/protocol/activity"><item><text>Hello, World!</text></item></publish></pubsub></iq>
conn DEBUG SENT: <r xmlns='urn:xmpp:sm:3'/>
xmpp DEBUG RECV: <message type="headline" to="dc@livedata.ammonit.com" from="dc@livedata.ammonit.com"><event xmlns="http://jabber.org/protocol/pubsub#event"><items node="http://jabber.org/protocol/activity"><item id="6AAD553375500"><text>Hello, World!</text></item></items></event><addresses xmlns="http://jabber.org/protocol/address"><address jid="dc@livedata.ammonit.com/caps-test" type="replyto"/></addresses></message>
xmpp DEBUG RECV: <r xmlns="urn:xmpp:sm:3"/>
conn DEBUG SENT: <a h="5" xmlns="urn:xmpp:sm:3"/>
xmpp DEBUG RECV: <iq id="current" type="result" to="dc@livedata.ammonit.com/caps-test" lang="en" from="dc@livedata.ammonit.com"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="http://jabber.org/protocol/activity"><item id="6AAD553375500"/></publish></pubsub></iq>

And here is the program:

#include <stdio.h>

#include <glib.h>
#include <strophe.h>

G_DEFINE_AUTOPTR_CLEANUP_FUNC(xmpp_ctx_t, xmpp_ctx_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(xmpp_conn_t, xmpp_conn_release)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(xmpp_stanza_t, xmpp_stanza_release)

static gint discover_capabilities_handler(xmpp_conn_t* conn, xmpp_stanza_t* iq, gpointer user_data) {
    xmpp_stanza_t* query = xmpp_stanza_get_child_by_name(iq, "query");

    if (query == NULL) {
        return TRUE;
    }

    const gchar* id = xmpp_stanza_get_id(iq);
    g_autoptr(xmpp_stanza_t) reply = xmpp_iq_new(xmpp_conn_get_context(conn), "result", id);
    xmpp_stanza_set_to(reply, xmpp_stanza_get_from(iq));
    query = xmpp_stanza_copy(query);
    xmpp_stanza_add_child(reply, query);
    g_autoptr(xmpp_stanza_t) identity = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(identity, "identity");
    xmpp_stanza_set_attribute(identity, "category", "client");
    xmpp_stanza_set_attribute(identity, "type", "bot");
    xmpp_stanza_add_child(query, identity);

    xmpp_send(conn, reply);
    return TRUE;
}

gint idle_handler(xmpp_conn_t* conn, gpointer user_data) {
    g_autoptr(xmpp_stanza_t) iq = xmpp_iq_new(xmpp_conn_get_context(conn), "set", "current");

    g_autoptr(xmpp_stanza_t) pubsub = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(pubsub, "pubsub");
    xmpp_stanza_set_ns(pubsub, "http://jabber.org/protocol/pubsub");
    xmpp_stanza_add_child(iq, pubsub);

    g_autoptr(xmpp_stanza_t) publish = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(publish, "publish");
    xmpp_stanza_set_attribute(publish, "node", "http://jabber.org/protocol/activity");
    xmpp_stanza_add_child(pubsub, publish);

    g_autoptr(xmpp_stanza_t) item = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(item, "item");
    xmpp_stanza_add_child(publish, item);

    g_autoptr(xmpp_stanza_t) text = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(text, "text");
    xmpp_stanza_add_child(item, text);

    g_autoptr(xmpp_stanza_t) inner = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_text(inner, "Hello, World!");
    xmpp_stanza_add_child(text, inner);

    xmpp_send(conn, iq);
    return TRUE;
}

void connection_handler(xmpp_conn_t* conn, xmpp_conn_event_t status, gint error, xmpp_stream_error_t* stream_error, gpointer user_data) {
    if (status != XMPP_CONN_CONNECT) {
        return;
    }

    g_autoptr(xmpp_stanza_t) presence = xmpp_presence_new(xmpp_conn_get_context(conn));
    g_autoptr(xmpp_stanza_t) capabilities = xmpp_stanza_new(xmpp_conn_get_context(conn));
    xmpp_stanza_set_name(capabilities, "c");
    xmpp_stanza_set_ns(capabilities, "http://jabber.org/protocol/caps");
    xmpp_stanza_set_attribute(capabilities, "hash", "sha-1");
    xmpp_stanza_set_attribute(capabilities, "node", "http://example.com/caps-test");
    xmpp_stanza_set_attribute(capabilities, "ver", "iaTkyKUZtplRPLviKWlgoSGj+Do=");
    xmpp_stanza_add_child(presence, capabilities);
    xmpp_send(conn, presence);

    xmpp_handler_add(conn, discover_capabilities_handler, NULL, "iq", "get", NULL);
    xmpp_timed_handler_add(conn, idle_handler, 1000, NULL);
}

gint main(gint argc, gchar** argv) {
    if (argc != 5) {
        fputs("Usage: ./a.out USERNAME SERVER PASSWORD\n", stderr);
        return EXIT_FAILURE;
    }

    gchar* username = argv[1];
    gchar* server = argv[2];
    gchar* password = argv[3];
    gint port = atoi(argv[4]);
    g_autofree gchar* jid = g_strdup_printf("%s@%s/caps-test", username, server);

    xmpp_initialize();
    xmpp_log_t* log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
    g_autoptr(xmpp_ctx_t) ctx = xmpp_ctx_new(NULL, log);
    g_autoptr(xmpp_conn_t) conn = xmpp_conn_new(ctx);
    xmpp_conn_set_flags(conn, XMPP_CONN_FLAG_LEGACY_SSL);
    xmpp_conn_set_jid(conn, jid);
    xmpp_conn_set_pass(conn, password);
    xmpp_connect_client(conn, server, port, connection_handler, NULL);

    xmpp_run(ctx);
    xmpp_shutdown();
    return EXIT_SUCCESS;
}
licaon-kter commented 5 months ago

Same with 23.10 from backports?

dansebcar commented 5 months ago

Yes, I installed 23.10-1~bpo12+1 and I still receive the <message type="headline">.

weiss commented 5 months ago

The server MUST NOT send notifications related to any NodeIDs that the contact's client has not asked for via the relevant "NodeID+notify" disco#info feature.

My understanding is that this clause is limited to the context of +notify subscriptions. The server must obviously still send notifications to explicit subscribers, for example. And, if auto-subscribe is supported (which it is for ejabberd's PEP), the spec says:

A service MAY auto-subscribe owners and publishers if they are not already subscribed

I'd guess that's what you're running into here.

However, I'd expect the owner to be able to explicitly unsubscribe, which we currently don't support in the PEP case. I think that's a bug (but unfortunately not trivial to fix).