00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 # include "config.h"
00025 #endif
00026
00027
00028 #include "digest-md5.h"
00029
00030
00031 #include <stdlib.h>
00032
00033
00034 #include <string.h>
00035
00036
00037 #include "nonascii.h"
00038 #include "tokens.h"
00039 #include "parser.h"
00040 #include "printer.h"
00041 #include "free.h"
00042 #include "session.h"
00043 #include "digesthmac.h"
00044
00045 #define CNONCE_ENTROPY_BYTES 16
00046
00047 struct _Gsasl_digest_md5_client_state
00048 {
00049 int step;
00050 unsigned long readseqnum, sendseqnum;
00051 char secret[DIGEST_MD5_LENGTH];
00052 char kic[DIGEST_MD5_LENGTH];
00053 char kcc[DIGEST_MD5_LENGTH];
00054 char kis[DIGEST_MD5_LENGTH];
00055 char kcs[DIGEST_MD5_LENGTH];
00056 digest_md5_challenge challenge;
00057 digest_md5_response response;
00058 digest_md5_finish finish;
00059 };
00060 typedef struct _Gsasl_digest_md5_client_state _Gsasl_digest_md5_client_state;
00061
00062 int
00063 _gsasl_digest_md5_client_start (Gsasl_session * sctx, void **mech_data)
00064 {
00065 _Gsasl_digest_md5_client_state *state;
00066 char nonce[CNONCE_ENTROPY_BYTES];
00067 char *p;
00068 int rc;
00069
00070 rc = gsasl_nonce (nonce, CNONCE_ENTROPY_BYTES);
00071 if (rc != GSASL_OK)
00072 return rc;
00073
00074 rc = gsasl_base64_to (nonce, CNONCE_ENTROPY_BYTES, &p, NULL);
00075 if (rc != GSASL_OK)
00076 return rc;
00077
00078 state = calloc (1, sizeof (*state));
00079 if (state == NULL)
00080 {
00081 free (p);
00082 return GSASL_MALLOC_ERROR;
00083 }
00084
00085 state->response.cnonce = p;
00086 state->response.nc = 1;
00087
00088 *mech_data = state;
00089
00090 return GSASL_OK;
00091 }
00092
00093 int
00094 _gsasl_digest_md5_client_step (Gsasl_session * sctx,
00095 void *mech_data,
00096 const char *input,
00097 size_t input_len,
00098 char **output, size_t * output_len)
00099 {
00100 _Gsasl_digest_md5_client_state *state = mech_data;
00101 int rc, res;
00102
00103 *output = NULL;
00104 *output_len = 0;
00105
00106 switch (state->step)
00107 {
00108 case 0:
00109 state->step++;
00110 if (input_len == 0)
00111 return GSASL_NEEDS_MORE;
00112
00113
00114 case 1:
00115 {
00116 if (digest_md5_parse_challenge (input, input_len,
00117 &state->challenge) < 0)
00118 return GSASL_MECHANISM_PARSE_ERROR;
00119
00120
00121
00122
00123
00124
00125
00126
00127 if (state->challenge.nrealms > 0)
00128 gsasl_property_set (sctx, GSASL_REALM, state->challenge.realms[0]);
00129 else
00130 gsasl_property_set (sctx, GSASL_REALM, NULL);
00131
00132
00133
00134
00135 state->response.utf8 = 1;
00136 state->response.qop = 1;
00137
00138 state->response.nonce = strdup (state->challenge.nonce);
00139 if (!state->response.nonce)
00140 return GSASL_MALLOC_ERROR;
00141
00142 {
00143 const char *service = gsasl_property_get (sctx, GSASL_SERVICE);
00144 const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME);
00145 if (!service)
00146 return GSASL_NO_SERVICE;
00147 if (!hostname)
00148 return GSASL_NO_HOSTNAME;
00149 if (asprintf (&state->response.digesturi, "%s/%s",
00150 service, hostname) < 0)
00151 return GSASL_MALLOC_ERROR;
00152 }
00153
00154 {
00155 const char *c;
00156 char *tmp, *tmp2;
00157
00158 c = gsasl_property_get (sctx, GSASL_AUTHID);
00159 if (!c)
00160 return GSASL_NO_AUTHID;
00161
00162 state->response.username = strdup (c);
00163 if (!state->response.username)
00164 return GSASL_MALLOC_ERROR;
00165
00166 c = gsasl_property_get (sctx, GSASL_AUTHZID);
00167 if (c)
00168 {
00169 state->response.authzid = strdup (c);
00170 if (!state->response.authzid)
00171 return GSASL_MALLOC_ERROR;
00172 }
00173
00174 gsasl_callback (NULL, sctx, GSASL_REALM);
00175 c = gsasl_property_fast (sctx, GSASL_REALM);
00176 if (c)
00177 {
00178 state->response.realm = strdup (c);
00179 if (!state->response.realm)
00180 return GSASL_MALLOC_ERROR;
00181 }
00182
00183 c = gsasl_property_get (sctx, GSASL_PASSWORD);
00184 if (!c)
00185 return GSASL_NO_PASSWORD;
00186
00187 tmp2 = utf8tolatin1ifpossible (c);
00188
00189 rc = asprintf (&tmp, "%s:%s:%s", state->response.username,
00190 state->response.realm ?
00191 state->response.realm : "", tmp2);
00192 free (tmp2);
00193 if (rc < 0)
00194 return GSASL_MALLOC_ERROR;
00195
00196 rc = gsasl_md5 (tmp, strlen (tmp), &tmp2);
00197 free (tmp);
00198 if (rc != GSASL_OK)
00199 return rc;
00200 memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH);
00201 free (tmp2);
00202 }
00203
00204 rc = digest_md5_hmac (state->response.response,
00205 state->secret,
00206 state->response.nonce,
00207 state->response.nc,
00208 state->response.cnonce,
00209 state->response.qop,
00210 state->response.authzid,
00211 state->response.digesturi,
00212 0,
00213 state->response.cipher,
00214 state->kic, state->kis, state->kcc, state->kcs);
00215 if (rc)
00216 return GSASL_CRYPTO_ERROR;
00217
00218 *output = digest_md5_print_response (&state->response);
00219 if (!*output)
00220 return GSASL_AUTHENTICATION_ERROR;
00221
00222 *output_len = strlen (*output);
00223
00224 state->step++;
00225 res = GSASL_NEEDS_MORE;
00226 }
00227 break;
00228
00229 case 2:
00230 {
00231 char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
00232
00233 if (digest_md5_parse_finish (input, input_len, &state->finish) < 0)
00234 return GSASL_MECHANISM_PARSE_ERROR;
00235
00236 res = digest_md5_hmac (check, state->secret,
00237 state->response.nonce, state->response.nc,
00238 state->response.cnonce, state->response.qop,
00239 state->response.authzid,
00240 state->response.digesturi, 1,
00241 state->response.cipher, NULL, NULL, NULL,
00242 NULL);
00243 if (res != GSASL_OK)
00244 break;
00245
00246 if (strcmp (state->finish.rspauth, check) == 0)
00247 res = GSASL_OK;
00248 else
00249 res = GSASL_AUTHENTICATION_ERROR;
00250 state->step++;
00251 }
00252 break;
00253
00254 default:
00255 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00256 break;
00257 }
00258
00259 return res;
00260 }
00261
00262 void
00263 _gsasl_digest_md5_client_finish (Gsasl_session * sctx, void *mech_data)
00264 {
00265 _Gsasl_digest_md5_client_state *state = mech_data;
00266
00267 if (!state)
00268 return;
00269
00270 digest_md5_free_challenge (&state->challenge);
00271 digest_md5_free_response (&state->response);
00272 digest_md5_free_finish (&state->finish);
00273
00274 free (state);
00275 }
00276
00277 int
00278 _gsasl_digest_md5_client_encode (Gsasl_session * sctx,
00279 void *mech_data,
00280 const char *input,
00281 size_t input_len,
00282 char **output, size_t * output_len)
00283 {
00284 _Gsasl_digest_md5_client_state *state = mech_data;
00285 int res;
00286
00287 res = digest_md5_encode (input, input_len, output, output_len,
00288 state->response.qop,
00289 state->sendseqnum, state->kic);
00290 if (res)
00291 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00292
00293 if (state->sendseqnum == 4294967295UL)
00294 state->sendseqnum = 0;
00295 else
00296 state->sendseqnum++;
00297
00298 return GSASL_OK;
00299 }
00300
00301 int
00302 _gsasl_digest_md5_client_decode (Gsasl_session * sctx,
00303 void *mech_data,
00304 const char *input,
00305 size_t input_len,
00306 char **output, size_t * output_len)
00307 {
00308 _Gsasl_digest_md5_client_state *state = mech_data;
00309 int res;
00310
00311 res = digest_md5_decode (input, input_len, output, output_len,
00312 state->response.qop,
00313 state->readseqnum, state->kis);
00314 if (res)
00315 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00316
00317 if (state->readseqnum == 4294967295UL)
00318 state->readseqnum = 0;
00319 else
00320 state->readseqnum++;
00321
00322 return GSASL_OK;
00323 }