LCOV - code coverage report
Current view: top level - builds/gnutls/coverage/gnutls-git/lib - str-idna.c (source / functions) Hit Total Coverage
Test: GnuTLS-3.6.14 Code Coverage Lines: 95 119 79.8 %
Date: 2020-10-30 04:50:48 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2017 Tim Rühsen
       3             :  * Copyright (C) 2016, 2017 Red Hat, Inc.
       4             :  *
       5             :  * Author: Nikos Mavrogiannopoulos
       6             :  *
       7             :  * This file is part of GnuTLS.
       8             :  *
       9             :  * The GnuTLS is free software; you can redistribute it and/or
      10             :  * modify it under the terms of the GNU Lesser General Public License
      11             :  * as published by the Free Software Foundation; either version 2.1 of
      12             :  * the License, or (at your option) any later version.
      13             :  *
      14             :  * This library is distributed in the hope that it will be useful, but
      15             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17             :  * Lesser General Public License for more details.
      18             :  *
      19             :  * You should have received a copy of the GNU Lesser General Public License
      20             :  * along with this program.  If not, see <https://www.gnu.org/licenses/>
      21             :  *
      22             :  */
      23             : 
      24             : #include "gnutls_int.h"
      25             : #include "errors.h"
      26             : #include "str.h"
      27             : #include <unistr.h>
      28             : 
      29             : #ifdef HAVE_LIBIDN2
      30             : 
      31             : # include <idn2.h>
      32             : 
      33             : # define ICAST char
      34             : 
      35             : /**
      36             :  * gnutls_idna_map:
      37             :  * @input: contain the UTF-8 formatted domain name
      38             :  * @ilen: the length of the provided string
      39             :  * @out: the result in an null-terminated allocated string
      40             :  * @flags: should be zero
      41             :  *
      42             :  * This function will convert the provided UTF-8 domain name, to
      43             :  * its IDNA mapping in an allocated variable. Note that depending on the flags the used gnutls
      44             :  * library was compiled with, the output of this function may vary (i.e.,
      45             :  * may be IDNA2008, or IDNA2003).
      46             :  *
      47             :  * To force IDNA2008 specify the flag %GNUTLS_IDNA_FORCE_2008. In
      48             :  * the case GnuTLS is not compiled with the necessary dependencies,
      49             :  * %GNUTLS_E_UNIMPLEMENTED_FEATURE will be returned to indicate that
      50             :  * gnutls is unable to perform the requested conversion.
      51             :  *
      52             :  * Note also, that this function will return an empty string if an
      53             :  * empty string is provided as input.
      54             :  *
      55             :  * Returns: %GNUTLS_E_INVALID_UTF8_STRING on invalid UTF-8 data, or 0 on success.
      56             :  *
      57             :  * Since: 3.5.8
      58             :  **/
      59       13129 : int gnutls_idna_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
      60             : {
      61       13129 :         char *idna = NULL;
      62       13129 :         int rc, ret;
      63       13129 :         gnutls_datum_t istr;
      64       13129 :         unsigned int idn2_flags = IDN2_NFC_INPUT;
      65       13129 :         unsigned int idn2_tflags = IDN2_NFC_INPUT;
      66             : 
      67             :         /* IDN2_NONTRANSITIONAL automatically converts to lowercase
      68             :          * IDN2_NFC_INPUT converts to NFC before toASCII conversion
      69             :          *
      70             :          * Since IDN2_NONTRANSITIONAL implicitly does NFC conversion, we don't need
      71             :          * the additional IDN2_NFC_INPUT. But just for the unlikely case that the linked
      72             :          * library is not matching the headers when building and it doesn't support TR46,
      73             :          * we provide IDN2_NFC_INPUT.
      74             :          *
      75             :          * Without IDN2_USE_STD3_ASCII_RULES, the result could contain any ASCII characters,
      76             :          * e.g. 'evil.c\u2100.example.com' will be converted into
      77             :          * 'evil.ca/c.example.com', which seems no good idea. */
      78       13129 :         idn2_flags |= IDN2_NONTRANSITIONAL | IDN2_USE_STD3_ASCII_RULES;
      79       13129 :         idn2_tflags |= IDN2_TRANSITIONAL | IDN2_USE_STD3_ASCII_RULES;
      80             : 
      81       13129 :         if (ilen == 0) {
      82           0 :                 out->data = (uint8_t*)gnutls_strdup("");
      83           0 :                 out->size = 0;
      84           0 :                 if (out->data == NULL)
      85           0 :                         return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
      86             :                 return 0;
      87             :         }
      88             : 
      89      161463 :         if (_gnutls_str_is_print(input, ilen)) {
      90       13074 :                 return _gnutls_set_strdatum(out, input, ilen);
      91             :         }
      92             : 
      93          55 :         ret = _gnutls_set_strdatum(&istr, input, ilen);
      94          55 :         if (ret < 0) {
      95           0 :                 gnutls_assert();
      96           0 :                 return ret;
      97             :         }
      98             : 
      99          55 :         rc = idn2_to_ascii_8z((ICAST*)istr.data, (ICAST**)&idna, idn2_flags);
     100          55 :         if (rc == IDN2_DISALLOWED && !(flags & GNUTLS_IDNA_FORCE_2008))
     101           1 :                 rc = idn2_to_ascii_8z((ICAST*)istr.data, (ICAST**)&idna, idn2_tflags);
     102             : 
     103          55 :         if (rc != IDN2_OK) {
     104           4 :                 gnutls_assert();
     105           4 :                 idna = NULL; /* in case idn2_lookup_u8 modifies &idna */
     106           4 :                 _gnutls_debug_log("unable to convert name '%s' to IDNA format: %s\n", istr.data, idn2_strerror(rc));
     107           4 :                 ret = GNUTLS_E_INVALID_UTF8_STRING;
     108           4 :                 goto fail;
     109             :         }
     110             : 
     111          51 :         if (gnutls_free != idn2_free) {
     112          51 :                 ret = _gnutls_set_strdatum(out, idna, strlen(idna));
     113             :         } else  {
     114           0 :                 out->data = (unsigned char*)idna;
     115           0 :                 out->size = strlen(idna);
     116           0 :                 idna = NULL;
     117           0 :                 ret = 0;
     118             :         }
     119             : 
     120          55 :  fail:
     121          55 :         idn2_free(idna);
     122          55 :         gnutls_free(istr.data);
     123          55 :         return ret;
     124             : }
     125             : 
     126             : /**
     127             :  * gnutls_idna_reverse_map:
     128             :  * @input: contain the ACE (IDNA) formatted domain name
     129             :  * @ilen: the length of the provided string
     130             :  * @out: the result in an null-terminated allocated UTF-8 string
     131             :  * @flags: should be zero
     132             :  *
     133             :  * This function will convert an ACE (ASCII-encoded) domain name to a UTF-8 domain name.
     134             :  *
     135             :  * If GnuTLS is compiled without IDNA support, then this function
     136             :  * will return %GNUTLS_E_UNIMPLEMENTED_FEATURE.
     137             :  *
     138             :  * Note also, that this function will return an empty string if an
     139             :  * empty string is provided as input.
     140             :  *
     141             :  * Returns: A negative error code on error, or 0 on success.
     142             :  *
     143             :  * Since: 3.5.8
     144             :  **/
     145         168 : int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
     146             : {
     147         168 :         char *u8 = NULL;
     148         168 :         int rc, ret;
     149         168 :         gnutls_datum_t istr;
     150             : 
     151         168 :         if (ilen == 0) {
     152           0 :                 out->data = (uint8_t*)gnutls_strdup("");
     153           0 :                 out->size = 0;
     154           0 :                 if (out->data == NULL)
     155           0 :                         return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
     156             :                 return 0;
     157             :         }
     158             : 
     159         168 :         ret = _gnutls_set_strdatum(&istr, input, ilen);
     160         168 :         if (ret < 0) {
     161           0 :                 gnutls_assert();
     162           0 :                 return ret;
     163             :         }
     164             : 
     165             :         /* currently libidn2 just converts single labels, thus a wrapper function */
     166         168 :         rc = idn2_to_unicode_8z8z((char*)istr.data, &u8, 0);
     167         168 :         if (rc != IDN2_OK) {
     168          40 :                 gnutls_assert();
     169          40 :                 _gnutls_debug_log("unable to convert ACE name '%s' to UTF-8 format: %s\n", istr.data, idn2_strerror(rc));
     170          40 :                 ret = GNUTLS_E_INVALID_UTF8_STRING;
     171          40 :                 goto fail;
     172             :         }
     173             : 
     174         128 :         if (gnutls_malloc != malloc) {
     175           0 :                 ret = _gnutls_set_strdatum(out, u8, strlen(u8));
     176             :         } else  {
     177         128 :                 out->data = (unsigned char*)u8;
     178         128 :                 out->size = strlen(u8);
     179         128 :                 u8 = NULL;
     180         128 :                 ret = 0;
     181             :         }
     182         168 :  fail:
     183         168 :         idn2_free(u8);
     184         168 :         gnutls_free(istr.data);
     185         168 :         return ret;
     186             : }
     187             : 
     188             : #else /* no HAVE_LIBIDN2 */
     189             : 
     190             : # undef gnutls_idna_map
     191             : int gnutls_idna_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
     192             : {
     193             :         if (!_gnutls_str_is_print(input, ilen)) {
     194             :                 return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
     195             :         }
     196             : 
     197             :         return _gnutls_set_strdatum(out, input, ilen);
     198             : }
     199             : 
     200             : int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags)
     201             : {
     202             :         return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
     203             : }
     204             : #endif /* HAVE_LIBIDN2 */
     205             : 
     206         122 : int _gnutls_idna_email_map(const char *input, unsigned ilen, gnutls_datum_t *output)
     207             : {
     208         122 :         const char *p = input;
     209             : 
     210         669 :         while(*p != 0 && *p != '@') {
     211         548 :                 if (!c_isprint(*p))
     212           1 :                         return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
     213         547 :                 p++;
     214             :         }
     215             : 
     216        1769 :         if (_gnutls_str_is_print(input, ilen)) {
     217         120 :                 return _gnutls_set_strdatum(output, input, ilen);
     218             :         }
     219             : 
     220           1 :         if (*p == '@') {
     221           1 :                 unsigned name_part = p-input;
     222           1 :                 int ret;
     223           1 :                 gnutls_datum_t domain;
     224             : 
     225           1 :                 ret = gnutls_idna_map(p+1, ilen-name_part-1, &domain, 0);
     226           1 :                 if (ret < 0)
     227           0 :                         return gnutls_assert_val(ret);
     228             : 
     229           1 :                 output->data = gnutls_malloc(name_part+1+domain.size+1);
     230           1 :                 if (output->data == NULL) {
     231           0 :                         gnutls_free(domain.data);
     232           0 :                         return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
     233             :                 }
     234           1 :                 memcpy(output->data, input, name_part);
     235           1 :                 output->data[name_part] = '@';
     236           1 :                 memcpy(&output->data[name_part+1], domain.data, domain.size);
     237           1 :                 output->data[name_part+domain.size+1] = 0;
     238           1 :                 output->size = name_part+domain.size+1;
     239           1 :                 gnutls_free(domain.data);
     240           1 :                 return 0;
     241             :         } else {
     242           0 :                 return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
     243             :         }
     244             : }
     245             : 
     246          34 : int _gnutls_idna_email_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *output)
     247             : {
     248          34 :         const char *p = input;
     249             : 
     250         580 :         while(*p != 0 && *p != '@') {
     251         546 :                 if (!c_isprint(*p))
     252           0 :                         return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
     253         546 :                 p++;
     254             :         }
     255             : 
     256          34 :         if (*p == '@') {
     257          12 :                 unsigned name_part = p-input;
     258          12 :                 int ret;
     259          12 :                 gnutls_datum_t domain;
     260             : 
     261          12 :                 ret = gnutls_idna_reverse_map(p+1, ilen-name_part-1, &domain, 0);
     262          12 :                 if (ret < 0)
     263           8 :                         return gnutls_assert_val(ret);
     264             : 
     265           4 :                 output->data = gnutls_malloc(name_part+1+domain.size+1);
     266           4 :                 if (output->data == NULL) {
     267           0 :                         gnutls_free(domain.data);
     268           0 :                         return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
     269             :                 }
     270           4 :                 memcpy(output->data, input, name_part);
     271           4 :                 output->data[name_part] = '@';
     272           4 :                 memcpy(&output->data[name_part+1], domain.data, domain.size);
     273           4 :                 output->data[name_part+domain.size+1] = 0;
     274           4 :                 output->size = name_part+domain.size+1;
     275           4 :                 gnutls_free(domain.data);
     276           4 :                 return 0;
     277             :         } else {
     278          22 :                 return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_EMAIL);
     279             :         }
     280             : }

Generated by: LCOV version 1.14