summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2021-02-11 16:43:47 +0300
committerdefanor <defanor@uberspace.net>2021-02-11 17:33:22 +0300
commit30b8528e17ea184a704229a82f0446b8a400ccfd (patch)
treeed9e1d1f4052f417eeec4c4d8ee4e733943cb493
parent6acda7ad1f834016c9cebea0dd82467db86baeeb (diff)
Add initial JID checks
The rexmpp_jid_check function now ensures that JID parts are valid UTF-8 strings, and that only allowed code points (per RFC 8265) are used in those. Though there is a few more checks to perform still.
-rw-r--r--README2
-rw-r--r--configure.ac4
-rw-r--r--examples/basic.c2
-rw-r--r--src/Makefile.am3
-rw-r--r--src/rexmpp.c4
-rw-r--r--src/rexmpp_jid.c115
-rw-r--r--src/rexmpp_jid.h1
7 files changed, 128 insertions, 3 deletions
diff --git a/README b/README
index a5a149c..6f317ef 100644
--- a/README
+++ b/README
@@ -15,7 +15,7 @@ of implementing additional XEPs on top of it, and should try to make
it easy to implement a decent client application using it.
Current dependencies: libunbound, libxml2, gnutls, gnutls-dane, gsasl,
-gpgme.
+gpgme, libicu.
A rough roadmap:
diff --git a/configure.ac b/configure.ac
index 02ea44c..c175e74 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,10 @@ AC_SUBST(UNBOUND_LIBS)
AM_PATH_GPGME
+PKG_CHECK_MODULES([ICU_I18N], [icu-i18n])
+AC_SUBST(ICU_I18N_CFLAGS)
+AC_SUBST(ICU_I18N_LIBS)
+
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h netdb.h netinet/in.h sys/socket.h syslog.h])
diff --git a/examples/basic.c b/examples/basic.c
index 123225a..404b281 100644
--- a/examples/basic.c
+++ b/examples/basic.c
@@ -106,6 +106,8 @@ main (int argc, char **argv) {
/* GNUTLS_X509_FMT_PEM); */
s.roster_cache_file = "roster.xml";
+ /* rexmpp_openpgp_set_home_dir(&s, "./pgp/"); */
+
/* Once the main structure is initialised and everything is
sufficiently configured, we are ready to run the main loop and
call rexmpp from it. */
diff --git a/src/Makefile.am b/src/Makefile.am
index ac34f06..52d6c4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,5 +21,6 @@ include_HEADERS = rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \
rexmpp_dns.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h
librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) \
$(LIBDANE_CFLAGS) $(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(GPGME_CFLAGS)
+ $(ICU_I18N_CFLAGS)
librexmpp_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(LIBDANE_LIBS) \
- $(GSASL_LIBS) $(UNBOUND_LIBS) $(GPGME_LIBS)
+ $(GSASL_LIBS) $(UNBOUND_LIBS) $(GPGME_LIBS) $(ICU_I18N_LIBS)
diff --git a/src/rexmpp.c b/src/rexmpp.c
index 03e666e..3064f3f 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -441,6 +441,10 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
rexmpp_log(s, LOG_CRIT, "Failed to parse the initial JID.");
return REXMPP_E_JID;
}
+ if (! rexmpp_jid_check(&s->initial_jid)) {
+ rexmpp_log(s, LOG_CRIT, "An invalid initial JID is provided.");
+ return REXMPP_E_JID;
+ }
s->xml_parser = xmlCreatePushParserCtxt(&sax, s, "", 0, NULL);
diff --git a/src/rexmpp_jid.c b/src/rexmpp_jid.c
index f19bd7c..ee7b326 100644
--- a/src/rexmpp_jid.c
+++ b/src/rexmpp_jid.c
@@ -8,10 +8,14 @@
#include <stddef.h>
#include <string.h>
+#include <stdio.h>
+#include <unicode/ustring.h>
+#include <unicode/uset.h>
+#include <unicode/uspoof.h>
#include "rexmpp_jid.h"
int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid) {
- const char *resource = NULL, *domain = NULL;
+ const char *resource = NULL, *domain = str;
size_t i;
size_t resource_len = 0, local_len = 0;
size_t domain_len, bare_len, full_len = strlen(str);
@@ -63,3 +67,112 @@ int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid) {
return 0;
}
+
+/* <https://tools.ietf.org/html/rfc7622#section-3>,
+ <https://tools.ietf.org/html/rfc8265#section-3.3> */
+int rexmpp_jid_check (struct rexmpp_jid *jid) {
+ UErrorCode err = U_ZERO_ERROR;
+ UChar local[1023], domain[1023], resource[1023];
+ int32_t local_len = 0, domain_len = 0, resource_len = 0;
+
+ /* Initial length checks are performed on parsing. */
+
+ /* Ensure that it's all valid UTF-8. */
+
+ u_strFromUTF8(local, 1023, &local_len, jid->local, -1, &err);
+ if (U_FAILURE(err)) {
+ return 0;
+ }
+ /* TODO: IDNA2008 on domain part. */
+ u_strFromUTF8(domain, 1023, &domain_len, jid->domain, -1, &err);
+ if (U_FAILURE(err)) {
+ return 0;
+ }
+ u_strFromUTF8(resource, 1023, &resource_len, jid->resource, -1, &err);
+ if (U_FAILURE(err)) {
+ return 0;
+ }
+
+ /* TODO: width mapping. */
+
+
+ /* Check character classes */
+ USpoofChecker *sc;
+ int32_t spoof;
+
+ /* IdentifierClass: {Ll, Lu, Lo, Nd, Lm, Mn, Mc} + 0x21 to 0x7e */
+ USet *identifier_chars = uset_openEmpty();
+ uset_applyIntPropertyValue(identifier_chars, UCHAR_GENERAL_CATEGORY_MASK,
+ U_GC_LL_MASK | U_GC_LU_MASK | U_GC_LO_MASK |
+ U_GC_ND_MASK | U_GC_LM_MASK | U_GC_MN_MASK |
+ U_GC_MC_MASK,
+ &err);
+ if (U_FAILURE(err)) {
+ return 0;
+ }
+ uset_addRange(identifier_chars, 0x21, 0x7e);
+
+ sc = uspoof_open(&err);
+ if (U_FAILURE(err)) {
+ uset_close(identifier_chars);
+ return 0;
+ }
+ uspoof_setChecks(sc, USPOOF_CHAR_LIMIT, &err);
+ if (U_FAILURE(err)) {
+ uset_close(identifier_chars);
+ uspoof_close(sc);
+ return 0;
+ }
+ uspoof_setAllowedChars(sc, identifier_chars, &err);
+ if (U_FAILURE(err)) {
+ uset_close(identifier_chars);
+ uspoof_close(sc);
+ return 0;
+ }
+ spoof = uspoof_check(sc, local, local_len, NULL, &err);
+ if (U_FAILURE(err) || spoof) {
+ uset_close(identifier_chars);
+ uspoof_close(sc);
+ return 0;
+ }
+
+ /* FreeformClass: Zs, {Sm, Sc, Sk, So}, {Pc, Pd, Ps, Pe, Pi, Pf,
+ Po}, toNFKC(cp) != cp ones, {Lt, Nl, No, Me}, and IdentifierClass
+ ones. */
+
+ /* TODO: https://tools.ietf.org/html/rfc8264#section-9.17 */
+ USet *freeform_chars = uset_openEmpty();
+ uset_applyIntPropertyValue(freeform_chars, UCHAR_GENERAL_CATEGORY_MASK,
+ U_GC_ZS_MASK | U_GC_SM_MASK | U_GC_SC_MASK |
+ U_GC_SK_MASK | U_GC_SO_MASK | U_GC_PC_MASK |
+ U_GC_PD_MASK | U_GC_PS_MASK | U_GC_PE_MASK |
+ U_GC_PI_MASK | U_GC_PF_MASK | U_GC_PO_MASK |
+ U_GC_LT_MASK | U_GC_NL_MASK | U_GC_NO_MASK |
+ U_GC_ME_MASK,
+ &err);
+ if (U_FAILURE(err)) {
+ uset_close(freeform_chars);
+ uset_close(identifier_chars);
+ uspoof_close(sc);
+ return 0;
+ }
+ uset_addAll(freeform_chars, identifier_chars);
+ uset_close(identifier_chars);
+
+ spoof = uspoof_check(sc, resource, resource_len, NULL, &err);
+ if (U_FAILURE(err) || spoof) {
+ uset_close(freeform_chars);
+ uspoof_close(sc);
+ return 0;
+ }
+ uset_close(freeform_chars);
+ uspoof_close(sc);
+
+ /* TODO: case mapping, u_strToLower */
+
+ /* TODO: normalization, unorm2_normalize */
+
+ /* TODO: directionality */
+
+ return 1;
+}
diff --git a/src/rexmpp_jid.h b/src/rexmpp_jid.h
index 8613e3d..bfeedcb 100644
--- a/src/rexmpp_jid.h
+++ b/src/rexmpp_jid.h
@@ -20,5 +20,6 @@ struct rexmpp_jid {
};
int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid);
+int rexmpp_jid_check (struct rexmpp_jid *jid);
#endif