|
Packit |
549fdc |
/*
|
|
Packit |
549fdc |
* Copyright (C) 2017 Tim Rühsen
|
|
Packit |
549fdc |
* Copyright (C) 2016, 2017 Red Hat, Inc.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Author: Nikos Mavrogiannopoulos
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This file is part of GnuTLS.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
549fdc |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
549fdc |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
549fdc |
* the License, or (at your option) any later version.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
549fdc |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
549fdc |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
549fdc |
* Lesser General Public License for more details.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
549fdc |
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#include "gnutls_int.h"
|
|
Packit |
549fdc |
#include "errors.h"
|
|
Packit |
549fdc |
#include "str.h"
|
|
Packit |
549fdc |
#include <unistr.h>
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#ifdef HAVE_LIBIDN2
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
# include <idn2.h>
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#if IDN2_VERSION_NUMBER < 0x02000000
|
|
Packit |
549fdc |
# define idn2_to_ascii_8z idn2_lookup_u8
|
|
Packit |
549fdc |
# define ICAST uint8_t
|
|
Packit |
549fdc |
#else
|
|
Packit |
549fdc |
# define ICAST char
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_idna_map:
|
|
Packit |
549fdc |
* @input: contain the UTF-8 formatted domain name
|
|
Packit |
549fdc |
* @ilen: the length of the provided string
|
|
Packit |
549fdc |
* @out: the result in an null-terminated allocated string
|
|
Packit |
549fdc |
* @flags: should be zero
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function will convert the provided UTF-8 domain name, to
|
|
Packit |
549fdc |
* its IDNA mapping in an allocated variable. Note that depending on the flags the used gnutls
|
|
Packit |
549fdc |
* library was compiled with, the output of this function may vary (i.e.,
|
|
Packit |
549fdc |
* may be IDNA2008, or IDNA2003).
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* To force IDNA2008 specify the flag %GNUTLS_IDNA_FORCE_2008. In
|
|
Packit |
549fdc |
* the case GnuTLS is not compiled with the necessary dependencies,
|
|
Packit |
549fdc |
* %GNUTLS_E_UNIMPLEMENTED_FEATURE will be returned to indicate that
|
|
Packit |
549fdc |
* gnutls is unable to perform the requested conversion.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Note also, that this function will return an empty string if an
|
|
Packit |
549fdc |
* empty string is provided as input.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: %GNUTLS_E_INVALID_UTF8_STRING on invalid UTF-8 data, or 0 on success.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.5.8
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
int gnutls_idna_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
char *idna = NULL;
|
|
Packit |
549fdc |
int rc, ret;
|
|
Packit |
549fdc |
gnutls_datum_t istr;
|
|
Packit |
549fdc |
unsigned int idn2_flags = IDN2_NFC_INPUT;
|
|
Packit |
549fdc |
unsigned int idn2_tflags = IDN2_NFC_INPUT;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#if IDN2_VERSION_NUMBER >= 0x00140000
|
|
Packit |
549fdc |
/* IDN2_NONTRANSITIONAL automatically converts to lowercase
|
|
Packit |
549fdc |
* IDN2_NFC_INPUT converts to NFC before toASCII conversion
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since IDN2_NONTRANSITIONAL implicitely does NFC conversion, we don't need
|
|
Packit |
549fdc |
* the additional IDN2_NFC_INPUT. But just for the unlikely case that the linked
|
|
Packit |
549fdc |
* library is not matching the headers when building and it doesn't support TR46,
|
|
Packit |
549fdc |
* we provide IDN2_NFC_INPUT. */
|
|
Packit |
549fdc |
idn2_flags |= IDN2_NONTRANSITIONAL;
|
|
Packit |
549fdc |
idn2_tflags |= IDN2_TRANSITIONAL;
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ilen == 0) {
|
|
Packit |
549fdc |
out->data = (uint8_t*)gnutls_strdup("");
|
|
Packit |
549fdc |
out->size = 0;
|
|
Packit |
549fdc |
if (out->data == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (_gnutls_str_is_print(input, ilen)) {
|
|
Packit |
549fdc |
return _gnutls_set_strdatum(out, input, ilen);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_set_strdatum(&istr, input, ilen);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
rc = idn2_to_ascii_8z((ICAST*)istr.data, (ICAST**)&idna, idn2_flags);
|
|
Packit |
549fdc |
if (rc == IDN2_DISALLOWED && !(flags & GNUTLS_IDNA_FORCE_2008))
|
|
Packit |
549fdc |
rc = idn2_to_ascii_8z((ICAST*)istr.data, (ICAST**)&idna, idn2_tflags);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (rc != IDN2_OK) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
idna = NULL; /* in case idn2_lookup_u8 modifies &idna */
|
|
Packit |
549fdc |
_gnutls_debug_log("unable to convert name '%s' to IDNA format: %s\n", istr.data, idn2_strerror(rc));
|
|
Packit |
549fdc |
ret = GNUTLS_E_INVALID_UTF8_STRING;
|
|
Packit |
549fdc |
goto fail;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (gnutls_free != idn2_free) {
|
|
Packit |
549fdc |
ret = _gnutls_set_strdatum(out, idna, strlen(idna));
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
out->data = (unsigned char*)idna;
|
|
Packit |
549fdc |
out->size = strlen(idna);
|
|
Packit |
549fdc |
idna = NULL;
|
|
Packit |
549fdc |
ret = 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
fail:
|
|
Packit |
549fdc |
idn2_free(idna);
|
|
Packit |
549fdc |
gnutls_free(istr.data);
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#if IDN2_VERSION_NUMBER < 0x02000000
|
|
Packit |
549fdc |
int _idn2_punycode_decode(
|
|
Packit |
549fdc |
size_t input_length,
|
|
Packit |
549fdc |
const char input[],
|
|
Packit |
549fdc |
size_t *output_length,
|
|
Packit |
549fdc |
uint32_t output[],
|
|
Packit |
549fdc |
unsigned char case_flags[]);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
static int idn2_to_unicode_8z8z(const char *src, char **dst, unsigned flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int rc, run;
|
|
Packit |
549fdc |
size_t out_len = 0;
|
|
Packit |
549fdc |
const char *e, *s;
|
|
Packit |
549fdc |
char *p = NULL;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
for (run = 0; run < 2; run++) {
|
|
Packit |
549fdc |
if (run) {
|
|
Packit |
549fdc |
p = malloc(out_len + 1);
|
|
Packit |
549fdc |
if (!p)
|
|
Packit |
549fdc |
return IDN2_MALLOC;
|
|
Packit |
549fdc |
*dst = p;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
out_len = 0;
|
|
Packit |
549fdc |
for (e = s = src; *e; s = e) {
|
|
Packit |
549fdc |
while (*e && *e != '.')
|
|
Packit |
549fdc |
e++;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (e - s > 4 && (s[0] == 'x' || s[0] == 'X') && (s[1] == 'n' || s[1] == 'N') && s[2] == '-' && s[3] == '-') {
|
|
Packit |
549fdc |
size_t u32len = IDN2_LABEL_MAX_LENGTH * 4;
|
|
Packit |
549fdc |
uint32_t u32[IDN2_LABEL_MAX_LENGTH * 4];
|
|
Packit |
549fdc |
uint8_t u8[IDN2_LABEL_MAX_LENGTH + 1];
|
|
Packit |
549fdc |
size_t u8len;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
rc = _idn2_punycode_decode(e - s - 4, s + 4, &u32len, u32, NULL);
|
|
Packit |
549fdc |
if (rc != IDN2_OK)
|
|
Packit |
549fdc |
return rc;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
u8len = sizeof(u8);
|
|
Packit |
549fdc |
if (u32_to_u8(u32, u32len, u8, &u8len) == NULL)
|
|
Packit |
549fdc |
return IDN2_ENCODING_ERROR;
|
|
Packit |
549fdc |
u8[u8len] = '\0';
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (run)
|
|
Packit |
549fdc |
memcpy(*dst + out_len, u8, u8len);
|
|
Packit |
549fdc |
out_len += u8len;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
if (run)
|
|
Packit |
549fdc |
memcpy(*dst + out_len, s, e - s);
|
|
Packit |
549fdc |
out_len += e - s;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (*e) {
|
|
Packit |
549fdc |
e++;
|
|
Packit |
549fdc |
if (run)
|
|
Packit |
549fdc |
(*dst)[out_len] = '.';
|
|
Packit |
549fdc |
out_len++;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
(*dst)[out_len] = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return IDN2_OK;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_idna_reverse_map:
|
|
Packit |
549fdc |
* @input: contain the ACE (IDNA) formatted domain name
|
|
Packit |
549fdc |
* @ilen: the length of the provided string
|
|
Packit |
549fdc |
* @out: the result in an null-terminated allocated UTF-8 string
|
|
Packit |
549fdc |
* @flags: should be zero
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function will convert an ACE (ASCII-encoded) domain name to a UTF-8 domain name.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If GnuTLS is compiled without IDNA support, then this function
|
|
Packit |
549fdc |
* will return %GNUTLS_E_UNIMPLEMENTED_FEATURE.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Note also, that this function will return an empty string if an
|
|
Packit |
549fdc |
* empty string is provided as input.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: A negative error code on error, or 0 on success.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.5.8
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
char *u8 = NULL;
|
|
Packit |
549fdc |
int rc, ret;
|
|
Packit |
549fdc |
gnutls_datum_t istr;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ilen == 0) {
|
|
Packit |
549fdc |
out->data = (uint8_t*)gnutls_strdup("");
|
|
Packit |
549fdc |
out->size = 0;
|
|
Packit |
549fdc |
if (out->data == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_set_strdatum(&istr, input, ilen);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* currently libidn2 just converts single labels, thus a wrapper function */
|
|
Packit |
549fdc |
rc = idn2_to_unicode_8z8z((char*)istr.data, &u8, 0);
|
|
Packit |
549fdc |
if (rc != IDN2_OK) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_debug_log("unable to convert ACE name '%s' to UTF-8 format: %s\n", istr.data, idn2_strerror(rc));
|
|
Packit |
549fdc |
ret = GNUTLS_E_INVALID_UTF8_STRING;
|
|
Packit |
549fdc |
goto fail;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (gnutls_malloc != malloc) {
|
|
Packit |
549fdc |
ret = _gnutls_set_strdatum(out, u8, strlen(u8));
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
out->data = (unsigned char*)u8;
|
|
Packit |
549fdc |
out->size = strlen(u8);
|
|
Packit |
549fdc |
u8 = NULL;
|
|
Packit |
549fdc |
ret = 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
fail:
|
|
Packit |
549fdc |
idn2_free(u8);
|
|
Packit |
549fdc |
gnutls_free(istr.data);
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#else /* no HAVE_LIBIDN2 */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
# undef gnutls_idna_map
|
|
Packit |
549fdc |
int gnutls_idna_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (!_gnutls_str_is_print(input, ilen)) {
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return _gnutls_set_strdatum(out, input, ilen);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
#endif /* HAVE_LIBIDN2 */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
int _gnutls_idna_email_map(const char *input, unsigned ilen, gnutls_datum_t *output)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
const char *p = input;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
while(*p != 0 && *p != '@') {
|
|
Packit |
549fdc |
if (!c_isprint(*p))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
|
|
Packit |
549fdc |
p++;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (_gnutls_str_is_print(input, ilen)) {
|
|
Packit |
549fdc |
return _gnutls_set_strdatum(output, input, ilen);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (*p == '@') {
|
|
Packit |
549fdc |
unsigned name_part = p-input;
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
gnutls_datum_t domain;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = gnutls_idna_map(p+1, ilen-name_part-1, &domain, 0);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
output->data = gnutls_malloc(name_part+1+domain.size+1);
|
|
Packit |
549fdc |
if (output->data == NULL) {
|
|
Packit |
549fdc |
gnutls_free(domain.data);
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
memcpy(output->data, input, name_part);
|
|
Packit |
549fdc |
output->data[name_part] = '@';
|
|
Packit |
549fdc |
memcpy(&output->data[name_part+1], domain.data, domain.size);
|
|
Packit |
549fdc |
output->data[name_part+domain.size+1] = 0;
|
|
Packit |
549fdc |
output->size = name_part+domain.size+1;
|
|
Packit |
549fdc |
gnutls_free(domain.data);
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
int _gnutls_idna_email_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *output)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
const char *p = input;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
while(*p != 0 && *p != '@') {
|
|
Packit |
549fdc |
if (!c_isprint(*p))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
|
|
Packit |
549fdc |
p++;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (*p == '@') {
|
|
Packit |
549fdc |
unsigned name_part = p-input;
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
gnutls_datum_t domain;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = gnutls_idna_reverse_map(p+1, ilen-name_part-1, &domain, 0);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
output->data = gnutls_malloc(name_part+1+domain.size+1);
|
|
Packit |
549fdc |
if (output->data == NULL) {
|
|
Packit |
549fdc |
gnutls_free(domain.data);
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
memcpy(output->data, input, name_part);
|
|
Packit |
549fdc |
output->data[name_part] = '@';
|
|
Packit |
549fdc |
memcpy(&output->data[name_part+1], domain.data, domain.size);
|
|
Packit |
549fdc |
output->data[name_part+domain.size+1] = 0;
|
|
Packit |
549fdc |
output->size = name_part+domain.size+1;
|
|
Packit |
549fdc |
gnutls_free(domain.data);
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|