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