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 | _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 | rc = gsasl_nonce (nonce, CNONCE_ENTROPY_BYTES); |
72 | if (rc != GSASL_OK) |
73 | return rc; |
74 | |
75 | rc = gsasl_base64_to (nonce, CNONCE_ENTROPY_BYTES, &p, NULL); |
76 | if (rc != GSASL_OK) |
77 | return rc; |
78 | |
79 | state = calloc (1, sizeof (*state)); |
80 | if (state == NULL) |
81 | { |
82 | free (p); |
83 | return GSASL_MALLOC_ERROR; |
84 | } |
85 | |
86 | state->response.cnonce = p; |
87 | state->response.nc = 1; |
88 | |
89 | *mech_data = state; |
90 | |
91 | return GSASL_OK; |
92 | } |
93 | |
94 | int |
95 | _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 | _Gsasl_digest_md5_client_state *state = mech_data; |
102 | int rc, res; |
103 | |
104 | *output = NULL; |
105 | *output_len = 0; |
106 | |
107 | switch (state->step) |
108 | { |
109 | case 0: |
110 | state->step++; |
111 | if (input_len == 0) |
112 | return GSASL_NEEDS_MORE; |
113 | /* fall through */ |
114 | |
115 | case 1: |
116 | { |
117 | if (digest_md5_parse_challenge (input, input_len, |
118 | &state->challenge) < 0) |
119 | 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 | if (state->challenge.nrealms > 0) |
129 | gsasl_property_set (sctx, GSASL_REALM, state->challenge.realms[0]); |
130 | else |
131 | gsasl_property_set (sctx, GSASL_REALM, NULL); |
132 | |
133 | /* FIXME: cipher, maxbuf. */ |
134 | |
135 | /* Create response token. */ |
136 | state->response.utf8 = 1; |
137 | |
138 | gsasl_property_set (sctx, GSASL_QOPS, |
139 | digest_md5_qops2qopstr (state->challenge.qops)); |
140 | |
141 | { |
142 | const char *qop = gsasl_property_get (sctx, GSASL_QOP); |
143 | |
144 | if (!qop) |
145 | state->response.qop = GSASL_QOP_AUTH; |
146 | else if (strcmp (qop, "qop-int" ) == 0) |
147 | state->response.qop = GSASL_QOP_AUTH_INT; |
148 | else if (strcmp (qop, "qop-auth" ) == 0) |
149 | state->response.qop = GSASL_QOP_AUTH; |
150 | else |
151 | /* We don't support confidentiality or unknown |
152 | keywords. */ |
153 | return GSASL_AUTHENTICATION_ERROR; |
154 | } |
155 | |
156 | state->response.nonce = strdup (state->challenge.nonce); |
157 | if (!state->response.nonce) |
158 | return GSASL_MALLOC_ERROR; |
159 | |
160 | { |
161 | const char *service = gsasl_property_get (sctx, GSASL_SERVICE); |
162 | const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME); |
163 | if (!service) |
164 | return GSASL_NO_SERVICE; |
165 | if (!hostname) |
166 | return GSASL_NO_HOSTNAME; |
167 | if (asprintf (&state->response.digesturi, "%s/%s" , |
168 | service, hostname) < 0) |
169 | return GSASL_MALLOC_ERROR; |
170 | } |
171 | |
172 | { |
173 | const char *c; |
174 | char *tmp, *tmp2; |
175 | |
176 | c = gsasl_property_get (sctx, GSASL_AUTHID); |
177 | if (!c) |
178 | return GSASL_NO_AUTHID; |
179 | |
180 | state->response.username = strdup (c); |
181 | if (!state->response.username) |
182 | return GSASL_MALLOC_ERROR; |
183 | |
184 | c = gsasl_property_get (sctx, GSASL_AUTHZID); |
185 | if (c) |
186 | { |
187 | state->response.authzid = strdup (c); |
188 | if (!state->response.authzid) |
189 | return GSASL_MALLOC_ERROR; |
190 | } |
191 | |
192 | gsasl_callback (NULL, sctx, GSASL_REALM); |
193 | c = gsasl_property_fast (sctx, GSASL_REALM); |
194 | if (c) |
195 | { |
196 | state->response.realm = strdup (c); |
197 | if (!state->response.realm) |
198 | return GSASL_MALLOC_ERROR; |
199 | } |
200 | |
201 | c = gsasl_property_get (sctx, GSASL_PASSWORD); |
202 | if (!c) |
203 | return GSASL_NO_PASSWORD; |
204 | |
205 | tmp2 = utf8tolatin1ifpossible (c); |
206 | |
207 | rc = asprintf (&tmp, "%s:%s:%s" , state->response.username, |
208 | state->response.realm ? |
209 | state->response.realm : "" , tmp2); |
210 | free (tmp2); |
211 | if (rc < 0) |
212 | return GSASL_MALLOC_ERROR; |
213 | |
214 | rc = gsasl_md5 (tmp, strlen (tmp), &tmp2); |
215 | free (tmp); |
216 | if (rc != GSASL_OK) |
217 | return rc; |
218 | memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH); |
219 | free (tmp2); |
220 | } |
221 | |
222 | rc = digest_md5_hmac (state->response.response, |
223 | state->secret, |
224 | state->response.nonce, |
225 | state->response.nc, |
226 | state->response.cnonce, |
227 | state->response.qop, |
228 | state->response.authzid, |
229 | state->response.digesturi, |
230 | 0, |
231 | state->response.cipher, |
232 | state->kic, state->kis, state->kcc, state->kcs); |
233 | if (rc) |
234 | return GSASL_CRYPTO_ERROR; |
235 | |
236 | *output = digest_md5_print_response (&state->response); |
237 | if (!*output) |
238 | return GSASL_AUTHENTICATION_ERROR; |
239 | |
240 | *output_len = strlen (*output); |
241 | |
242 | state->step++; |
243 | res = GSASL_NEEDS_MORE; |
244 | } |
245 | break; |
246 | |
247 | case 2: |
248 | { |
249 | char check[DIGEST_MD5_RESPONSE_LENGTH + 1]; |
250 | |
251 | if (digest_md5_parse_finish (input, input_len, &state->finish) < 0) |
252 | return GSASL_MECHANISM_PARSE_ERROR; |
253 | |
254 | res = digest_md5_hmac (check, state->secret, |
255 | state->response.nonce, state->response.nc, |
256 | state->response.cnonce, state->response.qop, |
257 | state->response.authzid, |
258 | state->response.digesturi, 1, |
259 | state->response.cipher, NULL, NULL, NULL, |
260 | NULL); |
261 | if (res != GSASL_OK) |
262 | break; |
263 | |
264 | if (strcmp (state->finish.rspauth, check) == 0) |
265 | res = GSASL_OK; |
266 | else |
267 | res = GSASL_AUTHENTICATION_ERROR; |
268 | state->step++; |
269 | } |
270 | break; |
271 | |
272 | default: |
273 | res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; |
274 | break; |
275 | } |
276 | |
277 | return res; |
278 | } |
279 | |
280 | void |
281 | _gsasl_digest_md5_client_finish (Gsasl_session * sctx, void *mech_data) |
282 | { |
283 | _Gsasl_digest_md5_client_state *state = mech_data; |
284 | |
285 | if (!state) |
286 | return; |
287 | |
288 | digest_md5_free_challenge (&state->challenge); |
289 | digest_md5_free_response (&state->response); |
290 | digest_md5_free_finish (&state->finish); |
291 | |
292 | free (state); |
293 | } |
294 | |
295 | int |
296 | _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 | _Gsasl_digest_md5_client_state *state = mech_data; |
303 | int res; |
304 | |
305 | res = digest_md5_encode (input, input_len, output, output_len, |
306 | state->response.qop, |
307 | state->sendseqnum, state->kic); |
308 | if (res) |
309 | return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR; |
310 | |
311 | if (state->sendseqnum == 4294967295UL) |
312 | state->sendseqnum = 0; |
313 | else |
314 | state->sendseqnum++; |
315 | |
316 | return GSASL_OK; |
317 | } |
318 | |
319 | int |
320 | _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 | _Gsasl_digest_md5_client_state *state = mech_data; |
327 | int res; |
328 | |
329 | res = digest_md5_decode (input, input_len, output, output_len, |
330 | state->response.qop, |
331 | state->readseqnum, state->kis); |
332 | if (res) |
333 | return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR; |
334 | |
335 | if (state->readseqnum == 4294967295UL) |
336 | state->readseqnum = 0; |
337 | else |
338 | state->readseqnum++; |
339 | |
340 | return GSASL_OK; |
341 | } |
342 | |