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.
27 /* Get specification. */
28 #include "digest-md5.h"
30 /* Get malloc, free. */
33 /* Get memcpy, strdup, strlen. */
42 #include "digesthmac.h"
45 #define NONCE_ENTROPY_BYTES 16
47 struct _Gsasl_digest_md5_server_state
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
;
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
];
70 rc
= gsasl_nonce (nonce
, NONCE_ENTROPY_BYTES
);
74 rc
= gsasl_base64_to (nonce
, NONCE_ENTROPY_BYTES
, &p
, NULL
);
78 state
= calloc (1, sizeof (*state
));
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;
97 _gsasl_digest_md5_server_step (Gsasl_session
* sctx
,
101 char **output
, size_t * output_len
)
103 _Gsasl_digest_md5_server_state
*state
= mech_data
;
115 c
= gsasl_property_get (sctx
, GSASL_REALM
);
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
);
136 return GSASL_AUTHENTICATION_ERROR
;
138 *output_len
= strlen (*output
);
140 res
= GSASL_NEEDS_MORE
;
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
164 c
= gsasl_property_get (sctx
, GSASL_PASSWORD
);
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
);
177 memcpy (state
->secret
, tmp2
, DIGEST_MD5_LENGTH
);
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
);
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
);
206 return GSASL_AUTHENTICATION_ERROR
;
208 *output
= digest_md5_print_finish (&state
->finish
);
210 return GSASL_MALLOC_ERROR
;
212 *output_len
= strlen (*output
);
215 res
= GSASL_NEEDS_MORE
;
225 res
= GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
;
233 _gsasl_digest_md5_server_finish (Gsasl_session
* sctx
, void *mech_data
)
235 _Gsasl_digest_md5_server_state
*state
= mech_data
;
240 digest_md5_free_challenge (&state
->challenge
);
241 digest_md5_free_response (&state
->response
);
242 digest_md5_free_finish (&state
->finish
);
248 _gsasl_digest_md5_server_encode (Gsasl_session
* sctx
,
252 char **output
, size_t * output_len
)
254 _Gsasl_digest_md5_server_state
*state
= mech_data
;
257 res
= digest_md5_encode (input
, input_len
, output
, output_len
,
258 state
->response
.qop
, state
->sendseqnum
,
261 return res
== -2 ? GSASL_NEEDS_MORE
: GSASL_INTEGRITY_ERROR
;
263 if (state
->sendseqnum
== 4294967295UL)
264 state
->sendseqnum
= 0;
272 _gsasl_digest_md5_server_decode (Gsasl_session
* sctx
,
276 char **output
, size_t * output_len
)
278 _Gsasl_digest_md5_server_state
*state
= mech_data
;
281 res
= digest_md5_decode (input
, input_len
, output
, output_len
,
282 state
->response
.qop
, state
->readseqnum
,
285 return res
== -2 ? GSASL_NEEDS_MORE
: GSASL_INTEGRITY_ERROR
;
287 if (state
->readseqnum
== 4294967295UL)
288 state
->readseqnum
= 0;