gsasl  2.2.1
digest-md5/client.c
Go to the documentation of this file.
1 /* client.c --- DIGEST-MD5 mechanism from RFC 2831, client 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, 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 "qop.h"
45 
46 #include "mechtools.h"
47 
48 #define CNONCE_ENTROPY_BYTES 16
49 
51 {
52  int step;
53  unsigned long readseqnum, sendseqnum;
62 };
64 
65 int
67  void **mech_data)
68 {
70  char nonce[CNONCE_ENTROPY_BYTES];
71  char *p;
72  int rc;
73 
75  if (rc != GSASL_OK)
76  return rc;
77 
78  rc = gsasl_base64_to (nonce, CNONCE_ENTROPY_BYTES, &p, NULL);
79  if (rc != GSASL_OK)
80  return rc;
81 
82  state = calloc (1, sizeof (*state));
83  if (state == NULL)
84  {
85  free (p);
86  return GSASL_MALLOC_ERROR;
87  }
88 
89  state->response.cnonce = p;
90  state->response.nc = 1;
91 
92  *mech_data = state;
93 
94  return GSASL_OK;
95 }
96 
97 int
99  void *mech_data,
100  const char *input,
101  size_t input_len,
102  char **output, size_t *output_len)
103 {
104  _Gsasl_digest_md5_client_state *state = mech_data;
105  int rc, res;
106 
107  *output = NULL;
108  *output_len = 0;
109 
110  if (state->step == 0)
111  {
112  state->step++;
113  if (input_len == 0)
114  return GSASL_NEEDS_MORE;
115  }
116 
117  switch (state->step)
118  {
119  case 1:
120  {
121  if (digest_md5_parse_challenge (input, input_len,
122  &state->challenge) < 0)
124 
125  /* FIXME: How to let application know of remaining realms?
126  One idea, add a GSASL_REALM_COUNT property, and have the
127  GSASL_REALM be that many concatenated zero terminated realm
128  strings. Slightly hackish, though. Another cleaner
129  approach would be to add gsasl_property_set_array and
130  gsasl_property_get_array APIs, for those properties that
131  may be used multiple times. */
132  if (state->challenge.nrealms > 0)
133  res = gsasl_property_set (sctx, GSASL_REALM,
134  state->challenge.realms[0]);
135  else
136  res = gsasl_property_set (sctx, GSASL_REALM, NULL);
137  if (res != GSASL_OK)
138  return res;
139 
140  /* FIXME: cipher, maxbuf. */
141 
142  /* Create response token. */
143  state->response.utf8 = 1;
144 
145  res = gsasl_property_set (sctx, GSASL_QOPS,
146  digest_md5_qops2qopstr (state->
147  challenge.qops));
148  if (res != GSASL_OK)
149  return res;
150 
151  {
152  const char *qop = gsasl_property_get (sctx, GSASL_QOP);
153 
154  if (!qop)
156  else if (strcmp (qop, "qop-int") == 0)
158  else if (strcmp (qop, "qop-auth") == 0)
160  else
161  /* We don't support confidentiality or unknown
162  keywords. */
164  }
165 
166  state->response.nonce = strdup (state->challenge.nonce);
167  if (!state->response.nonce)
168  return GSASL_MALLOC_ERROR;
169 
170  {
171  const char *service = gsasl_property_get (sctx, GSASL_SERVICE);
172  const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME);
173  if (!service)
174  return GSASL_NO_SERVICE;
175  if (!hostname)
176  return GSASL_NO_HOSTNAME;
177  if (asprintf (&state->response.digesturi, "%s/%s",
178  service, hostname) < 0)
179  return GSASL_MALLOC_ERROR;
180  }
181 
182  {
183  const char *c;
184  char *tmp, *tmp2;
185 
186  c = gsasl_property_get (sctx, GSASL_AUTHID);
187  if (!c)
188  return GSASL_NO_AUTHID;
189 
190  state->response.username = strdup (c);
191  if (!state->response.username)
192  return GSASL_MALLOC_ERROR;
193 
194  c = gsasl_property_get (sctx, GSASL_AUTHZID);
195  if (c)
196  {
197  state->response.authzid = strdup (c);
198  if (!state->response.authzid)
199  return GSASL_MALLOC_ERROR;
200  }
201 
202  gsasl_callback (NULL, sctx, GSASL_REALM);
203  c = gsasl_property_fast (sctx, GSASL_REALM);
204  if (c)
205  {
206  state->response.realm = strdup (c);
207  if (!state->response.realm)
208  return GSASL_MALLOC_ERROR;
209  }
210 
212  if (!c)
213  return GSASL_NO_PASSWORD;
214 
215  tmp2 = utf8tolatin1ifpossible (c);
216 
217  rc = asprintf (&tmp, "%s:%s:%s", state->response.username,
218  state->response.realm ?
219  state->response.realm : "", tmp2);
220  free (tmp2);
221  if (rc < 0)
222  return GSASL_MALLOC_ERROR;
223 
224  rc = gc_md5 (tmp, strlen (tmp), state->secret);
225  free (tmp);
226  if (rc != GC_OK)
227  return GSASL_CRYPTO_ERROR;
228  }
229 
231  state->secret,
232  state->response.nonce,
233  state->response.nc,
234  state->response.cnonce,
235  state->response.qop,
236  state->response.authzid,
237  state->response.digesturi,
238  0,
239  state->response.cipher,
240  state->kic, state->kis, state->kcc, state->kcs);
241  if (rc)
242  return GSASL_CRYPTO_ERROR;
243 
244  *output = digest_md5_print_response (&state->response);
245  if (!*output)
247 
248  *output_len = strlen (*output);
249 
250  state->step++;
251  res = GSASL_NEEDS_MORE;
252  }
253  break;
254 
255  case 2:
256  {
257  char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
258 
259  if (digest_md5_parse_finish (input, input_len, &state->finish) < 0)
261 
262  res = digest_md5_hmac (check, state->secret,
263  state->response.nonce, state->response.nc,
264  state->response.cnonce, state->response.qop,
265  state->response.authzid,
266  state->response.digesturi, 1,
267  state->response.cipher, NULL, NULL, NULL,
268  NULL);
269  if (res != GSASL_OK)
270  break;
271 
272  if (strcmp (state->finish.rspauth, check) == 0)
273  res = GSASL_OK;
274  else
276  state->step++;
277  }
278  break;
279 
280  default:
282  break;
283  }
284 
285  return res;
286 }
287 
288 void
290  void *mech_data)
291 {
292  _Gsasl_digest_md5_client_state *state = mech_data;
293 
294  if (!state)
295  return;
296 
299  digest_md5_free_finish (&state->finish);
300 
301  free (state);
302 }
303 
304 int
306  void *mech_data,
307  const char *input,
308  size_t input_len,
309  char **output, size_t *output_len)
310 {
311  _Gsasl_digest_md5_client_state *state = mech_data;
312  int res;
313 
314  res = digest_md5_encode (input, input_len, output, output_len,
315  state->response.qop,
316  state->sendseqnum, state->kic);
317  if (res)
318  return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
319 
320  if (state->sendseqnum == 4294967295UL)
321  state->sendseqnum = 0;
322  else
323  state->sendseqnum++;
324 
325  return GSASL_OK;
326 }
327 
328 int
330  void *mech_data,
331  const char *input,
332  size_t input_len,
333  char **output, size_t *output_len)
334 {
335  _Gsasl_digest_md5_client_state *state = mech_data;
336  int res;
337 
338  res = digest_md5_decode (input, input_len, output, output_len,
339  state->response.qop,
340  state->readseqnum, state->kis);
341  if (res)
342  return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
343 
344  if (state->readseqnum == 4294967295UL)
345  state->readseqnum = 0;
346  else
347  state->readseqnum++;
348 
349  return GSASL_OK;
350 }
int gsasl_base64_to(const char *in, size_t inlen, char **out, size_t *outlen)
Definition: base64.c:45
int gsasl_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
Definition: callback.c:71
int gsasl_nonce(char *data, size_t datalen)
Definition: crypto.c:39
int _gsasl_digest_md5_client_decode(Gsasl_session *sctx _GL_UNUSED, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
int _gsasl_digest_md5_client_encode(Gsasl_session *sctx _GL_UNUSED, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
int _gsasl_digest_md5_client_step(Gsasl_session *sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
void _gsasl_digest_md5_client_finish(Gsasl_session *sctx _GL_UNUSED, void *mech_data)
#define CNONCE_ENTROPY_BYTES
int _gsasl_digest_md5_client_start(Gsasl_session *sctx _GL_UNUSED, void **mech_data)
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_challenge(const char *challenge, size_t len, digest_md5_challenge *out)
int digest_md5_parse_finish(const char *finish, size_t len, digest_md5_finish *out)
char * digest_md5_print_response(digest_md5_response *r)
@ DIGEST_MD5_QOP_AUTH_INT
@ DIGEST_MD5_QOP_AUTH
#define DIGEST_MD5_RESPONSE_LENGTH
#define DIGEST_MD5_LENGTH
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_NO_SERVICE
Definition: gsasl.h:149
@ GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
Definition: gsasl.h:132
@ GSASL_NO_HOSTNAME
Definition: gsasl.h:150
@ GSASL_NO_AUTHID
Definition: gsasl.h:144
@ 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_HOSTNAME
Definition: gsasl.h:229
@ GSASL_AUTHZID
Definition: gsasl.h:225
@ GSASL_QOP
Definition: gsasl.h:237
@ GSASL_SERVICE
Definition: gsasl.h:228
@ GSASL_QOPS
Definition: gsasl.h:236
@ GSASL_PASSWORD
Definition: gsasl.h:226
@ GSASL_REALM
Definition: gsasl.h:234
@ GSASL_AUTHID
Definition: gsasl.h:224
_GSASL_API const char * gsasl_property_fast(Gsasl_session *sctx, Gsasl_property prop)
Definition: property.c:262
char * utf8tolatin1ifpossible(const char *passwd)
Definition: nonascii.c:67
const char * digest_md5_qops2qopstr(int qops)
Definition: qop.c:90
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 kcc[DIGEST_MD5_LENGTH]
char kcs[DIGEST_MD5_LENGTH]
char kic[DIGEST_MD5_LENGTH]
char secret[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]