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