LCOV - code coverage report
Current view: top level - lib/src - mechtools.c (source / functions) Hit Total Coverage
Test: GNU SASL Lines: 108 141 76.6 %
Date: 2022-09-15 21:30:29 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* mechtools.c --- Helper functions available for use by any mechanism.
       2             :  * Copyright (C) 2010-2022 Simon Josefsson
       3             :  *
       4             :  * This file is part of GNU SASL Library.
       5             :  *
       6             :  * GNU SASL Library is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public License
       8             :  * as published by the Free Software Foundation; either version 2.1 of
       9             :  * the License, or (at your option) any later version.
      10             :  *
      11             :  * GNU SASL Library is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with GNU SASL Library; if not, write to the Free
      18             :  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
      19             :  * Boston, MA 02110-1301, USA.
      20             :  *
      21             :  */
      22             : 
      23             : #ifdef HAVE_CONFIG_H
      24             : # include "config.h"
      25             : #endif
      26             : 
      27             : /* Get specification. */
      28             : #include "mechtools.h"
      29             : 
      30             : /* Get strcmp. */
      31             : #include <string.h>
      32             : 
      33             : /* Get malloc, free. */
      34             : #include <stdlib.h>
      35             : 
      36             : /* Get asprintf. */
      37             : #include <stdio.h>
      38             : 
      39             : /* Get error codes. */
      40             : #include <gsasl.h>
      41             : 
      42             : /* Gnulib gc.h */
      43             : #include <gc.h>
      44             : 
      45             : /* Create in AUTHZID a newly allocated copy of STR where =2C is
      46             :    replaced with , and =3D is replaced with =.  Return GSASL_OK on
      47             :    success, GSASL_MALLOC_ERROR on memory errors, GSASL_PARSE_ERRORS if
      48             :    string contains any unencoded ',' or incorrectly encoded
      49             :    sequence.  */
      50             : static int
      51           3 : unescape_authzid (const char *str, size_t len, char **authzid)
      52             : {
      53             :   char *p;
      54             : 
      55           3 :   if (memchr (str, ',', len) != NULL)
      56           0 :     return GSASL_MECHANISM_PARSE_ERROR;
      57             : 
      58           3 :   p = *authzid = malloc (len + 1);
      59           3 :   if (!p)
      60           0 :     return GSASL_MALLOC_ERROR;
      61             : 
      62          15 :   while (len > 0 && *str)
      63             :     {
      64          12 :       if (len >= 3 && str[0] == '=' && str[1] == '2' && str[2] == 'C')
      65             :         {
      66           0 :           *p++ = ',';
      67           0 :           str += 3;
      68           0 :           len -= 3;
      69             :         }
      70          12 :       else if (len >= 3 && str[0] == '=' && str[1] == '3' && str[2] == 'D')
      71             :         {
      72           0 :           *p++ = '=';
      73           0 :           str += 3;
      74           0 :           len -= 3;
      75             :         }
      76          12 :       else if (str[0] == '=')
      77             :         {
      78           0 :           free (*authzid);
      79           0 :           *authzid = NULL;
      80           0 :           return GSASL_MECHANISM_PARSE_ERROR;
      81             :         }
      82             :       else
      83             :         {
      84          12 :           *p++ = *str;
      85          12 :           str++;
      86          12 :           len--;
      87             :         }
      88             :     }
      89           3 :   *p = '\0';
      90             : 
      91           3 :   return GSASL_OK;
      92             : }
      93             : 
      94             : /* Parse the GS2 header containing flags and authorization identity.
      95             :    Put authorization identity (or NULL) in AUTHZID and length of
      96             :    header in HEADERLEN.  Return GSASL_OK on success or an error
      97             :    code.*/
      98             : int
      99          14 : _gsasl_parse_gs2_header (const char *data, size_t len,
     100             :                          char **authzid, size_t *headerlen)
     101             : {
     102             :   char *authzid_endptr;
     103             : 
     104          14 :   if (len < 3)
     105           4 :     return GSASL_MECHANISM_PARSE_ERROR;
     106             : 
     107          10 :   if (strncmp (data, "n,,", 3) == 0)
     108             :     {
     109           3 :       *headerlen = 3;
     110           3 :       *authzid = NULL;
     111             :     }
     112           7 :   else if (strncmp (data, "n,a=", 4) == 0 &&
     113           3 :            (authzid_endptr = memchr (data + 4, ',', len - 4)))
     114           3 :     {
     115             :       int res;
     116             : 
     117           3 :       if (authzid_endptr == NULL)
     118           0 :         return GSASL_MECHANISM_PARSE_ERROR;
     119             : 
     120           3 :       res = unescape_authzid (data + 4, authzid_endptr - (data + 4), authzid);
     121           3 :       if (res != GSASL_OK)
     122           0 :         return res;
     123             : 
     124           3 :       *headerlen = authzid_endptr - data + 1;
     125             :     }
     126             :   else
     127           4 :     return GSASL_MECHANISM_PARSE_ERROR;
     128             : 
     129           6 :   return GSASL_OK;
     130             : }
     131             : 
     132             : /* Return newly allocated copy of STR with all occurrences of ','
     133             :    replaced with =2C and '=' with '=3D', or return NULL on memory
     134             :    allocation errors.  */
     135             : static char *
     136           3 : escape_authzid (const char *str)
     137             : {
     138           3 :   char *out = malloc (strlen (str) * 3 + 1);
     139           3 :   char *p = out;
     140             : 
     141           3 :   if (!out)
     142           0 :     return NULL;
     143             : 
     144          15 :   while (*str)
     145             :     {
     146          12 :       if (*str == ',')
     147             :         {
     148           0 :           memcpy (p, "=2C", 3);
     149           0 :           p += 3;
     150             :         }
     151          12 :       else if (*str == '=')
     152             :         {
     153           0 :           memcpy (p, "=3D", 3);
     154           0 :           p += 3;
     155             :         }
     156             :       else
     157             :         {
     158          12 :           *p = *str;
     159          12 :           p++;
     160             :         }
     161          12 :       str++;
     162             :     }
     163           3 :   *p = '\0';
     164             : 
     165           3 :   return out;
     166             : }
     167             : 
     168             : /* Generate a newly allocated GS2 header, escaping authzid
     169             :    appropriately, and appending EXTRA. */
     170             : int
     171           6 : _gsasl_gs2_generate_header (bool nonstd, char cbflag,
     172             :                             const char *cbname, const char *authzid,
     173             :                             size_t extralen, const char *extra,
     174             :                             char **gs2h, size_t *gs2hlen)
     175             : {
     176           6 :   int elen = extralen;
     177             :   char *gs2cbflag;
     178             :   int len;
     179             : 
     180           6 :   if (cbflag == 'p')
     181           0 :     len = asprintf (&gs2cbflag, "p=%s", cbname);
     182           6 :   else if (cbflag == 'n')
     183           6 :     len = asprintf (&gs2cbflag, "n");
     184           0 :   else if (cbflag == 'y')
     185           0 :     len = asprintf (&gs2cbflag, "y");
     186             :   else
     187             :     /* internal caller error */
     188           0 :     return GSASL_MECHANISM_PARSE_ERROR;
     189             : 
     190           6 :   if (len <= 0 || gs2cbflag == NULL)
     191           0 :     return GSASL_MALLOC_ERROR;
     192             : 
     193           6 :   if (authzid)
     194             :     {
     195           3 :       char *escaped_authzid = escape_authzid (authzid);
     196             : 
     197           3 :       if (!escaped_authzid)
     198             :         {
     199           0 :           free (gs2cbflag);
     200           0 :           return GSASL_MALLOC_ERROR;
     201             :         }
     202             : 
     203           3 :       len = asprintf (gs2h, "%s%s,a=%s,%.*s", nonstd ? "F," : "",
     204             :                       gs2cbflag, escaped_authzid, elen, extra);
     205             : 
     206           3 :       free (escaped_authzid);
     207             :     }
     208             :   else
     209           3 :     len = asprintf (gs2h, "%s%s,,%.*s", nonstd ? "F," : "", gs2cbflag,
     210             :                     elen, extra);
     211             : 
     212           6 :   free (gs2cbflag);
     213             : 
     214           6 :   if (len <= 0 || gs2h == NULL)
     215           0 :     return GSASL_MALLOC_ERROR;
     216             : 
     217           6 :   *gs2hlen = len;
     218             : 
     219           6 :   return GSASL_OK;
     220             : }
     221             : 
     222             : /* Hex encode binary octet array IN of INLEN length, putting the hex
     223             :    encoded string in OUT which must have room for the data and
     224             :    terminating zero, i.e., 2*INLEN+1. */
     225             : void
     226          94 : _gsasl_hex_encode (const char *in, size_t inlen, char *out)
     227             : {
     228             :   static const char trans[] = "0123456789abcdef";
     229             : 
     230        2094 :   while (inlen--)
     231             :     {
     232        2000 :       unsigned char c = *in++;
     233        2000 :       *out++ = trans[(c >> 4) & 0xf];
     234        2000 :       *out++ = trans[c & 0xf];
     235             :     }
     236             : 
     237          94 :   *out = '\0';
     238          94 : }
     239             : 
     240             : static char
     241         388 : hexdigit_to_char (char hexdigit)
     242             : {
     243         388 :   if (hexdigit >= '0' && hexdigit <= '9')
     244         247 :     return hexdigit - '0';
     245         141 :   if (hexdigit >= 'a' && hexdigit <= 'f')
     246         141 :     return hexdigit - 'a' + 10;
     247           0 :   return 0;
     248             : }
     249             : 
     250             : static char
     251         194 : hex_to_char (char u, char l)
     252             : {
     253         194 :   return (char) (((unsigned char) hexdigit_to_char (u)) * 16
     254         194 :                  + hexdigit_to_char (l));
     255             : }
     256             : 
     257             : /* Hex decode string HEXSTR containing only hex "0-9A-F" characters
     258             :    into binary buffer BIN which must have room for data, i.e., strlen
     259             :    (hexstr)/2. */
     260             : void
     261          11 : _gsasl_hex_decode (const char *hexstr, char *bin)
     262             : {
     263         205 :   while (*hexstr)
     264             :     {
     265         194 :       *bin = hex_to_char (hexstr[0], hexstr[1]);
     266         194 :       hexstr += 2;
     267         194 :       bin++;
     268             :     }
     269          11 : }
     270             : 
     271             : /* Return whether string contains hex "0-9a-f" symbols only. */
     272             : bool
     273          11 : _gsasl_hex_p (const char *hexstr)
     274             : {
     275             :   static const char hexalpha[] = "0123456789abcdef";
     276             : 
     277         399 :   for (; *hexstr; hexstr++)
     278         388 :     if (strchr (hexalpha, *hexstr) == NULL)
     279           0 :       return false;
     280             : 
     281          11 :   return true;
     282             : }
     283             : 
     284             : /*
     285             :  * _gsasl_hash:
     286             :  * @hash: a %Gsasl_hash hash algorithm identifier, e.g. #GSASL_HASH_SHA256.
     287             :  * @in: input character array of data to hash.
     288             :  * @inlen: length of input character array of data to hash.
     289             :  * @outhash: buffer to hold hash of data.
     290             :  *
     291             :  * Compute hash of data using the @hash algorithm.  The @outhash
     292             :  * buffer must have room to hold the size of @hash's output; a safe
     293             :  * value that have room for all possible outputs is
     294             :  * %GSASL_HASH_MAX_SIZE.
     295             :  *
     296             :  * Return value: Returns %GSASL_OK iff successful.
     297             :  *
     298             :  * Since: 1.10
     299             :  **/
     300             : int
     301         148 : _gsasl_hash (Gsasl_hash hash, const char *in, size_t inlen, char *outhash)
     302             : {
     303             :   int rc;
     304             : 
     305         148 :   if (hash == GSASL_HASH_SHA1)
     306         133 :     rc = gc_sha1 (in, inlen, outhash);
     307          15 :   else if (hash == GSASL_HASH_SHA256)
     308          15 :     rc = gc_sha256 (in, inlen, outhash);
     309             :   else
     310           0 :     rc = GSASL_CRYPTO_ERROR;
     311             : 
     312         148 :   return rc;
     313             : }
     314             : 
     315             : /*
     316             :  * _gsasl_hmac:
     317             :  * @hash: a %Gsasl_hash hash algorithm identifier, e.g. #GSASL_HASH_SHA256.
     318             :  * @key: input character array with key to use.
     319             :  * @keylen: length of input character array with key to use.
     320             :  * @in: input character array of data to hash.
     321             :  * @inlen: length of input character array of data to hash.
     322             :  * @outhash: buffer to hold keyed hash of data.
     323             :  *
     324             :  * Compute keyed checksum of data using HMAC for the @hash algorithm.
     325             :  * The @outhash buffer must have room to hold the size of @hash's
     326             :  * output; a safe value that have room for all possible outputs is
     327             :  * %GSASL_HASH_MAX_SIZE.
     328             :  *
     329             :  * Return value: Returns %GSASL_OK iff successful.
     330             :  *
     331             :  * Since: 1.10
     332             :  **/
     333             : int
     334         397 : _gsasl_hmac (Gsasl_hash hash,
     335             :              const char *key, size_t keylen,
     336             :              const char *in, size_t inlen, char *outhash)
     337             : {
     338             :   int rc;
     339             : 
     340         397 :   if (hash == GSASL_HASH_SHA1)
     341         355 :     rc = gc_hmac_sha1 (key, keylen, in, inlen, outhash);
     342          42 :   else if (hash == GSASL_HASH_SHA256)
     343          42 :     rc = gc_hmac_sha256 (key, keylen, in, inlen, outhash);
     344             :   else
     345           0 :     rc = GSASL_CRYPTO_ERROR;
     346             : 
     347         397 :   return rc;
     348             : }
     349             : 
     350             : /*
     351             :  * gsasl_pbkdf2:
     352             :  * @hash: a %Gsasl_hash hash algorithm identifier.
     353             :  * @password: input character array with password to use.
     354             :  * @passwordlen: length of @password.
     355             :  * @salt: input character array with salt, typically a short string.
     356             :  * @saltlen: length of @salt.
     357             :  * @c: iteration count, typically larger than 4096.
     358             :  * @dk: output buffer, must be able to hold @dklen.
     359             :  * @dklen: length of output buffer, or 0 to indicate @hash output size.
     360             :  *
     361             :  * Hash and salt password according to PBKDF2 algorithm with the @hash
     362             :  * function used in HMAC.  This function can be used to prepare SCRAM
     363             :  * SaltedPassword values for the %GSASL_SCRAM_SALTED_PASSWORD
     364             :  * property.  Note that password should normally be prepared using
     365             :  * gsasl_saslprep(GSASL_ALLOW_UNASSIGNED) before calling this
     366             :  * function.
     367             :  *
     368             :  * Return value: Returns %GSASL_OK if successful, or error code.
     369             :  *
     370             :  * Since: 1.10
     371             :  **/
     372             : int
     373          94 : _gsasl_pbkdf2 (Gsasl_hash hash,
     374             :                const char *password, size_t passwordlen,
     375             :                const char *salt, size_t saltlen,
     376             :                unsigned int c, char *dk, size_t dklen)
     377             : {
     378             :   int rc;
     379             :   Gc_hash gch;
     380             : 
     381          94 :   switch (hash)
     382             :     {
     383          84 :     case GSASL_HASH_SHA1:
     384          84 :       if (dklen == 0)
     385          84 :         dklen = GSASL_HASH_SHA1_SIZE;
     386          84 :       gch = GC_SHA1;
     387          84 :       break;
     388             : 
     389          10 :     case GSASL_HASH_SHA256:
     390          10 :       if (dklen == 0)
     391          10 :         dklen = GSASL_HASH_SHA256_SIZE;
     392          10 :       gch = GC_SHA256;
     393          10 :       break;
     394             : 
     395           0 :     default:
     396           0 :       return GSASL_CRYPTO_ERROR;
     397             :     }
     398             : 
     399          94 :   rc = gc_pbkdf2_hmac (gch, password, passwordlen,
     400             :                        salt, saltlen, c, dk, dklen);
     401          94 :   if (rc != GC_OK)
     402           0 :     return GSASL_CRYPTO_ERROR;
     403             : 
     404          94 :   return GSASL_OK;
     405             : }

Generated by: LCOV version 1.16