gsasl  2.2.1
digest-md5/server.c
Go to the documentation of this file.
1 /* server.c --- DIGEST-MD5 mechanism from RFC 2831, server side.
2  * Copyright (C) 2002-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 "digest-md5.h"
27 
28 /* Get malloc, free. */
29 #include <stdlib.h>
30 
31 /* Get memcpy, strdup, strlen. */
32 #include <string.h>
33 
34 #include "gc.h"
35 
36 /* Get tools. */
37 #include "nonascii.h"
38 #include "tokens.h"
39 #include "parser.h"
40 #include "printer.h"
41 #include "free.h"
42 #include "session.h"
43 #include "digesthmac.h"
44 #include "validate.h"
45 #include "qop.h"
46 
47 #include "mechtools.h"
48 
49 #define NONCE_ENTROPY_BYTES 16
50 
52 {
53  int step;
54  unsigned long readseqnum, sendseqnum;
63 };
65 
66 int
68  void **mech_data)
69 {
71  char nonce[NONCE_ENTROPY_BYTES];
72  char *p;
73  int rc;
74 
76  if (rc != GSASL_OK)
77  return rc;
78 
79  rc = gsasl_base64_to (nonce, NONCE_ENTROPY_BYTES, &p, NULL);
80  if (rc != GSASL_OK)
81  return rc;
82 
83  state = calloc (1, sizeof (*state));
84  if (state == NULL)
85  {
86  free (p);
87  return GSASL_MALLOC_ERROR;
88  }
89 
91  state->challenge.ciphers = 0;
92 
93  state->challenge.nonce = p;
94  state->challenge.utf8 = 1;
95 
96  *mech_data = state;
97 
98  return GSASL_OK;
99 }
100 
101 static char
102 _gsasl_digest_md5_hexdigit_to_char (char hexdigit)
103 {
104  /* The hex representation always contains lowercase alphabetic
105  characters. See RFC 2831, 1.1. */
106 
107  if (hexdigit >= '0' && hexdigit <= '9')
108  return hexdigit - '0';
109  if (hexdigit >= 'a' && hexdigit <= 'z')
110  return hexdigit - 'a' + 10;
111 
112  return -1;
113 }
114 
115 static char
116 _gsasl_digest_md5_hex_to_char (char u, char l)
117 {
118  return (char) (((unsigned char) _gsasl_digest_md5_hexdigit_to_char (u)) *
119  16 + _gsasl_digest_md5_hexdigit_to_char (l));
120 }
121 
122 static int
123 _gsasl_digest_md5_set_hashed_secret (char *secret, const char *hex_secret)
124 {
125  /* Convert the hex string containing the secret to a byte array */
126  const char *p;
127  char *s;
128 
129  if (!hex_secret)
131 
132  s = secret;
133  p = hex_secret;
134  while (*p)
135  {
136  *s = _gsasl_digest_md5_hex_to_char (p[0], p[1]);
137  s++;
138 
139  p += 2;
140  }
141 
142  return GSASL_OK;
143 }
144 
145 int
147  void *mech_data,
148  const char *input,
149  size_t input_len,
150  char **output, size_t *output_len)
151 {
152  _Gsasl_digest_md5_server_state *state = mech_data;
153  int rc, res;
154 
155  *output = NULL;
156  *output_len = 0;
157 
158  switch (state->step)
159  {
160  case 0:
161  /* Set realm. */
162  {
163  const char *c;
164  c = gsasl_property_get (sctx, GSASL_REALM);
165  if (c)
166  {
167  state->challenge.nrealms = 1;
168 
169  state->challenge.realms =
170  malloc (sizeof (*state->challenge.realms));
171  if (!state->challenge.realms)
172  return GSASL_MALLOC_ERROR;
173 
174  state->challenge.realms[0] = strdup (c);
175  if (!state->challenge.realms[0])
176  return GSASL_MALLOC_ERROR;
177  }
178  }
179 
180  /* Set QOP */
181  {
182  const char *qopstr = gsasl_property_get (sctx, GSASL_QOPS);
183 
184  if (qopstr)
185  {
186  int qops = digest_md5_qopstr2qops (qopstr);
187 
188  if (qops == -1)
189  return GSASL_MALLOC_ERROR;
190 
191  /* We don't support confidentiality right now. */
192  if (qops & DIGEST_MD5_QOP_AUTH_CONF)
194 
195  if (qops)
196  state->challenge.qops = qops;
197  }
198  }
199 
200  /* FIXME: cipher, maxbuf, more realms. */
201 
202  /* Create challenge. */
203  *output = digest_md5_print_challenge (&state->challenge);
204  if (!*output)
206 
207  *output_len = strlen (*output);
208  state->step++;
209  res = GSASL_NEEDS_MORE;
210  break;
211 
212  case 1:
213  if (digest_md5_parse_response (input, input_len, &state->response) < 0)
215 
216  /* Make sure response is consistent with challenge. */
217  if (digest_md5_validate (&state->challenge, &state->response) < 0)
219 
220  /* Store properties, from the client response. */
221  if (state->response.utf8)
222  {
223  res = gsasl_property_set (sctx, GSASL_AUTHID,
224  state->response.username);
225  if (res != GSASL_OK)
226  return res;
227 
228  res = gsasl_property_set (sctx, GSASL_REALM, state->response.realm);
229  if (res != GSASL_OK)
230  return res;
231  }
232  else
233  {
234  /* Client provided username/realm in ISO-8859-1 form,
235  convert it to UTF-8 since the library is all-UTF-8. */
236  char *tmp;
237 
238  tmp = latin1toutf8 (state->response.username);
239  if (!tmp)
240  return GSASL_MALLOC_ERROR;
241  res = gsasl_property_set (sctx, GSASL_AUTHID, tmp);
242  free (tmp);
243  if (res != GSASL_OK)
244  return res;
245 
246  tmp = latin1toutf8 (state->response.realm);
247  if (!tmp)
248  return GSASL_MALLOC_ERROR;
249  res = gsasl_property_set (sctx, GSASL_REALM, tmp);
250  free (tmp);
251  if (res != GSASL_OK)
252  return res;
253  }
254 
255  res = gsasl_property_set (sctx, GSASL_AUTHZID, state->response.authzid);
256  if (res != GSASL_OK)
257  return res;
258 
259  /* FIXME: cipher, maxbuf. */
260 
261  /* Compute secret. */
262  {
263  const char *passwd;
264  const char *hashed_passwd;
265 
266  hashed_passwd =
268  if (hashed_passwd)
269  {
270  if (strlen (hashed_passwd) != (DIGEST_MD5_LENGTH * 2))
272 
273  rc = _gsasl_digest_md5_set_hashed_secret (state->secret,
274  hashed_passwd);
275  if (rc != GSASL_OK)
276  return rc;
277  }
278  else if ((passwd = gsasl_property_get (sctx, GSASL_PASSWORD)) != NULL)
279  {
280  char *tmp, *tmp2;
281 
282  tmp2 = utf8tolatin1ifpossible (passwd);
283 
284  rc = asprintf (&tmp, "%s:%s:%s", state->response.username,
285  state->response.realm ?
286  state->response.realm : "", tmp2);
287  free (tmp2);
288  if (rc < 0)
289  return GSASL_MALLOC_ERROR;
290 
291  rc = gc_md5 (tmp, strlen (tmp), state->secret);
292  free (tmp);
293  if (rc != GC_OK)
294  return GSASL_CRYPTO_ERROR;
295  }
296  else
297  {
298  return GSASL_NO_PASSWORD;
299  }
300  }
301 
302  /* Check client response. */
303  {
304  char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
305 
306  rc = digest_md5_hmac (check, state->secret,
307  state->response.nonce, state->response.nc,
308  state->response.cnonce, state->response.qop,
309  state->response.authzid,
310  state->response.digesturi, 0,
311  state->response.cipher,
312  state->kic, state->kis, state->kcc, state->kcs);
313  if (rc)
315 
316  if (strcmp (state->response.response, check) != 0)
318  }
319 
320  /* Create finish token. */
321  rc = digest_md5_hmac (state->finish.rspauth, state->secret,
322  state->response.nonce, state->response.nc,
323  state->response.cnonce, state->response.qop,
324  state->response.authzid,
325  state->response.digesturi, 1,
326  state->response.cipher, NULL, NULL, NULL, NULL);
327  if (rc)
329 
330  *output = digest_md5_print_finish (&state->finish);
331  if (!*output)
332  return GSASL_MALLOC_ERROR;
333 
334  *output_len = strlen (*output);
335 
336  state->step++;
337  res = GSASL_OK;
338  break;
339 
340  default:
342  break;
343  }
344 
345  return res;
346 }
347 
348 void
350  void *mech_data)
351 {
352  _Gsasl_digest_md5_server_state *state = mech_data;
353 
354  if (!state)
355  return;
356 
359  digest_md5_free_finish (&state->finish);
360 
361  free (state);
362 }
363 
364 int
366  void *mech_data,
367  const char *input,
368  size_t input_len,
369  char **output, size_t *output_len)
370 {
371  _Gsasl_digest_md5_server_state *state = mech_data;
372  int res;
373 
374  res = digest_md5_encode (input, input_len, output, output_len,
375  state->response.qop, state->sendseqnum,
376  state->kis);
377  if (res)
378  return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
379 
380  if (state->sendseqnum == 4294967295UL)
381  state->sendseqnum = 0;
382  else
383  state->sendseqnum++;
384 
385  return GSASL_OK;
386 }
387 
388 int
390  void *mech_data,
391  const char *input,
392  size_t input_len,
393  char **output, size_t *output_len)
394 {
395  _Gsasl_digest_md5_server_state *state = mech_data;
396  int res;
397 
398  res = digest_md5_decode (input, input_len, output, output_len,
399  state->response.qop, state->readseqnum,
400  state->kic);
401  if (res)
402  return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
403 
404  if (state->readseqnum == 4294967295UL)
405  state->readseqnum = 0;
406  else
407  state->readseqnum++;
408 
409  return GSASL_OK;
410 }
int gsasl_base64_to(const char *in, size_t inlen, char **out, size_t *outlen)
Definition: base64.c:45
int gsasl_nonce(char *data, size_t datalen)
Definition: crypto.c:39
void digest_md5_free_finish(digest_md5_finish *f)
void digest_md5_free_response(digest_md5_response *r)
void digest_md5_free_challenge(digest_md5_challenge *c)
int digest_md5_parse_response(const char *response, size_t len, digest_md5_response *out)
char * digest_md5_print_finish(digest_md5_finish *finish)
char * digest_md5_print_challenge(digest_md5_challenge *c)
int _gsasl_digest_md5_server_start(Gsasl_session *sctx _GL_UNUSED, void **mech_data)
int _gsasl_digest_md5_server_step(Gsasl_session *sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
int _gsasl_digest_md5_server_decode(Gsasl_session *sctx _GL_UNUSED, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
#define NONCE_ENTROPY_BYTES
void _gsasl_digest_md5_server_finish(Gsasl_session *sctx _GL_UNUSED, void *mech_data)
int _gsasl_digest_md5_server_encode(Gsasl_session *sctx _GL_UNUSED, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
@ DIGEST_MD5_QOP_AUTH_CONF
@ DIGEST_MD5_QOP_AUTH
#define DIGEST_MD5_RESPONSE_LENGTH
#define DIGEST_MD5_LENGTH
int digest_md5_validate(digest_md5_challenge *c, digest_md5_response *r)
int digest_md5_hmac(char *output, char secret[MD5LEN], const char *nonce, unsigned long nc, const char *cnonce, digest_md5_qop qop, const char *authzid, const char *digesturi, int rspauth, digest_md5_cipher cipher, char *kic, char *kis, char *kcc, char *kcs)
Definition: digesthmac.c:77
int rc
Definition: error.c:37
@ GSASL_OK
Definition: gsasl.h:129
@ GSASL_AUTHENTICATION_ERROR
Definition: gsasl.h:138
@ GSASL_NEEDS_MORE
Definition: gsasl.h:130
@ GSASL_MALLOC_ERROR
Definition: gsasl.h:133
@ GSASL_NO_PASSWORD
Definition: gsasl.h:146
@ GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
Definition: gsasl.h:132
@ GSASL_MECHANISM_PARSE_ERROR
Definition: gsasl.h:137
@ GSASL_CRYPTO_ERROR
Definition: gsasl.h:135
@ GSASL_INTEGRITY_ERROR
Definition: gsasl.h:139
_GSASL_API int gsasl_property_set(Gsasl_session *sctx, Gsasl_property prop, const char *data)
Definition: property.c:189
_GSASL_API const char * gsasl_property_get(Gsasl_session *sctx, Gsasl_property prop)
Definition: property.c:292
@ GSASL_DIGEST_MD5_HASHED_PASSWORD
Definition: gsasl.h:235
@ GSASL_AUTHZID
Definition: gsasl.h:225
@ GSASL_QOPS
Definition: gsasl.h:236
@ GSASL_PASSWORD
Definition: gsasl.h:226
@ GSASL_REALM
Definition: gsasl.h:234
@ GSASL_AUTHID
Definition: gsasl.h:224
char * latin1toutf8(const char *str)
Definition: nonascii.c:39
char * utf8tolatin1ifpossible(const char *passwd)
Definition: nonascii.c:67
int digest_md5_qopstr2qops(const char *qopstr)
Definition: qop.c:35
int digest_md5_decode(const char *input, size_t input_len, char **output, size_t *output_len, digest_md5_qop qop, unsigned long readseqnum, char key[DIGEST_MD5_LENGTH])
Definition: session.c:120
int digest_md5_encode(const char *input, size_t input_len, char **output, size_t *output_len, digest_md5_qop qop, unsigned long sendseqnum, char key[DIGEST_MD5_LENGTH])
Definition: session.c:46
char kis[DIGEST_MD5_LENGTH]
char kcs[DIGEST_MD5_LENGTH]
char kcc[DIGEST_MD5_LENGTH]
char secret[DIGEST_MD5_LENGTH]
char kic[DIGEST_MD5_LENGTH]
digest_md5_challenge challenge
char rspauth[DIGEST_MD5_RESPONSE_LENGTH+1]
digest_md5_cipher cipher
char response[DIGEST_MD5_RESPONSE_LENGTH+1]