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