diff options
author | defanor <defanor@uberspace.net> | 2021-02-11 16:43:47 +0300 |
---|---|---|
committer | defanor <defanor@uberspace.net> | 2021-02-11 17:33:22 +0300 |
commit | 30b8528e17ea184a704229a82f0446b8a400ccfd (patch) | |
tree | ed9e1d1f4052f417eeec4c4d8ee4e733943cb493 | |
parent | 6acda7ad1f834016c9cebea0dd82467db86baeeb (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-- | README | 2 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | examples/basic.c | 2 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/rexmpp.c | 4 | ||||
-rw-r--r-- | src/rexmpp_jid.c | 115 | ||||
-rw-r--r-- | src/rexmpp_jid.h | 1 |
7 files changed, 128 insertions, 3 deletions
@@ -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 |