Doc fix.
[gsasl.git] / lib / digest-md5 / server.c
blob7c557603e6d75e853a4d31959c389ca32a3be0ae
1 /* server.c --- DIGEST-MD5 mechanism from RFC 2831, server side.
2 * Copyright (C) 2002, 2003, 2004, 2006 Simon Josefsson
4 * This file is part of GNU SASL Library.
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.
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.
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.
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 /* Get specification. */
28 #include "digest-md5.h"
30 /* Get malloc, free. */
31 #include <stdlib.h>
33 /* Get memcpy, strdup, strlen. */
34 #include <string.h>
36 /* Get tools. */
37 #include "tokens.h"
38 #include "parser.h"
39 #include "printer.h"
40 #include "free.h"
41 #include "session.h"
42 #include "digesthmac.h"
43 #include "validate.h"
45 #define NONCE_ENTROPY_BYTES 16
47 struct _Gsasl_digest_md5_server_state
49 int step;
50 unsigned long readseqnum, sendseqnum;
51 char secret[DIGEST_MD5_LENGTH];
52 char kic[DIGEST_MD5_LENGTH];
53 char kcc[DIGEST_MD5_LENGTH];
54 char kis[DIGEST_MD5_LENGTH];
55 char kcs[DIGEST_MD5_LENGTH];
56 digest_md5_challenge challenge;
57 digest_md5_response response;
58 digest_md5_finish finish;
60 typedef struct _Gsasl_digest_md5_server_state _Gsasl_digest_md5_server_state;
62 int
63 _gsasl_digest_md5_server_start (Gsasl_session * sctx, void **mech_data)
65 _Gsasl_digest_md5_server_state *state;
66 char nonce[NONCE_ENTROPY_BYTES];
67 char *p;
68 int rc;
70 rc = gsasl_nonce (nonce, NONCE_ENTROPY_BYTES);
71 if (rc != GSASL_OK)
72 return rc;
74 rc = gsasl_base64_to (nonce, NONCE_ENTROPY_BYTES, &p, NULL);
75 if (rc != GSASL_OK)
76 return rc;
78 state = calloc (1, sizeof (*state));
79 if (state == NULL)
81 free (p);
82 return GSASL_MALLOC_ERROR;
85 state->challenge.qops = DIGEST_MD5_QOP_AUTH | DIGEST_MD5_QOP_AUTH_INT;
86 state->challenge.ciphers = 0;
88 state->challenge.nonce = p;
89 state->challenge.utf8 = 1;
91 *mech_data = state;
93 return GSASL_OK;
96 int
97 _gsasl_digest_md5_server_step (Gsasl_session * sctx,
98 void *mech_data,
99 const char *input,
100 size_t input_len,
101 char **output, size_t * output_len)
103 _Gsasl_digest_md5_server_state *state = mech_data;
104 int rc, res;
106 *output = NULL;
107 *output_len = 0;
109 switch (state->step)
111 case 0:
112 /* Set realm. */
114 const char *c;
115 c = gsasl_property_get (sctx, GSASL_REALM);
116 if (c)
118 state->challenge.nrealms = 1;
120 state->challenge.realms =
121 malloc (sizeof (*state->challenge.realms));
122 if (!state->challenge.realms)
123 return GSASL_MALLOC_ERROR;
125 state->challenge.realms[0] = strdup (c);
126 if (!state->challenge.realms[0])
127 return GSASL_MALLOC_ERROR;
131 /* FIXME: qop, cipher, maxbuf, more realms. */
133 /* Create challenge. */
134 *output = digest_md5_print_challenge (&state->challenge);
135 if (!*output)
136 return GSASL_AUTHENTICATION_ERROR;
138 *output_len = strlen (*output);
139 state->step++;
140 res = GSASL_NEEDS_MORE;
141 break;
143 case 1:
144 if (digest_md5_parse_response (input, input_len, &state->response) < 0)
145 return GSASL_MECHANISM_PARSE_ERROR;
147 /* Make sure response is consistent with challenge. */
148 if (digest_md5_validate (&state->challenge, &state->response) < 0)
149 return GSASL_MECHANISM_PARSE_ERROR;
151 /* Store properties, from the client response. */
152 gsasl_property_set (sctx, GSASL_AUTHID, state->response.username);
153 gsasl_property_set (sctx, GSASL_AUTHZID, state->response.authzid);
154 gsasl_property_set (sctx, GSASL_REALM, state->response.realm);
156 /* FIXME: qop, cipher, maxbuf. */
158 /* Compute secret. TODO: Add callback to retrieve hashed
159 secret. */
161 const char *c;
162 char *tmp, *tmp2;
164 c = gsasl_property_get (sctx, GSASL_PASSWORD);
165 if (!c)
166 return GSASL_NO_PASSWORD;
168 if (asprintf (&tmp, "%s:%s:%s", state->response.username,
169 state->response.realm ?
170 state->response.realm : "", c) < 0)
171 return GSASL_MALLOC_ERROR;
173 rc = gsasl_md5 (tmp, strlen (tmp), &tmp2);
174 free (tmp);
175 if (rc != GSASL_OK)
176 return rc;
177 memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH);
178 free (tmp2);
181 /* Check client response. */
183 char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
185 rc = digest_md5_hmac (check, state->secret,
186 state->response.nonce, state->response.nc,
187 state->response.cnonce, state->response.qop,
188 state->response.authzid,
189 state->response.digesturi, 0,
190 state->response.cipher, NULL, NULL, NULL, NULL);
191 if (rc)
192 return GSASL_AUTHENTICATION_ERROR;
194 if (strcmp (state->response.response, check) != 0)
195 return GSASL_AUTHENTICATION_ERROR;
198 /* Create finish token. */
199 rc = digest_md5_hmac (state->finish.rspauth, state->secret,
200 state->response.nonce, state->response.nc,
201 state->response.cnonce, state->response.qop,
202 state->response.authzid,
203 state->response.digesturi, 1,
204 state->response.cipher, NULL, NULL, NULL, NULL);
205 if (rc)
206 return GSASL_AUTHENTICATION_ERROR;
208 *output = digest_md5_print_finish (&state->finish);
209 if (!*output)
210 return GSASL_MALLOC_ERROR;
212 *output_len = strlen (*output);
214 state->step++;
215 res = GSASL_NEEDS_MORE;
216 break;
218 case 2:
219 *output_len = 0;
220 state->step++;
221 res = GSASL_OK;
222 break;
224 default:
225 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
226 break;
229 return res;
232 void
233 _gsasl_digest_md5_server_finish (Gsasl_session * sctx, void *mech_data)
235 _Gsasl_digest_md5_server_state *state = mech_data;
237 if (!state)
238 return;
240 digest_md5_free_challenge (&state->challenge);
241 digest_md5_free_response (&state->response);
242 digest_md5_free_finish (&state->finish);
244 free (state);
248 _gsasl_digest_md5_server_encode (Gsasl_session * sctx,
249 void *mech_data,
250 const char *input,
251 size_t input_len,
252 char **output, size_t * output_len)
254 _Gsasl_digest_md5_server_state *state = mech_data;
255 int res;
257 res = digest_md5_encode (input, input_len, output, output_len,
258 state->response.qop, state->sendseqnum,
259 state->kis);
260 if (res)
261 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
263 if (state->sendseqnum == 4294967295UL)
264 state->sendseqnum = 0;
265 else
266 state->sendseqnum++;
268 return GSASL_OK;
272 _gsasl_digest_md5_server_decode (Gsasl_session * sctx,
273 void *mech_data,
274 const char *input,
275 size_t input_len,
276 char **output, size_t * output_len)
278 _Gsasl_digest_md5_server_state *state = mech_data;
279 int res;
281 res = digest_md5_decode (input, input_len, output, output_len,
282 state->response.qop, state->readseqnum,
283 state->kic);
284 if (res)
285 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
287 if (state->readseqnum == 4294967295UL)
288 state->readseqnum = 0;
289 else
290 state->readseqnum++;
292 return GSASL_OK;