|
gsasl
1.8.0
|
00001 /* client.c --- DIGEST-MD5 mechanism from RFC 2831, client side. 00002 * Copyright (C) 2002-2012 Simon Josefsson 00003 * 00004 * This file is part of GNU SASL Library. 00005 * 00006 * GNU SASL Library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public License 00008 * as published by the Free Software Foundation; either version 2.1 of 00009 * the License, or (at your option) any later version. 00010 * 00011 * GNU SASL Library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with GNU SASL Library; if not, write to the Free 00018 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 * 00021 */ 00022 00023 #ifdef HAVE_CONFIG_H 00024 #include "config.h" 00025 #endif 00026 00027 /* Get specification. */ 00028 #include "digest-md5.h" 00029 00030 /* Get malloc, free. */ 00031 #include <stdlib.h> 00032 00033 /* Get memcpy, strlen. */ 00034 #include <string.h> 00035 00036 /* Get tools. */ 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 #include "qop.h" 00045 00046 #define CNONCE_ENTROPY_BYTES 16 00047 00048 struct _Gsasl_digest_md5_client_state 00049 { 00050 int step; 00051 unsigned long readseqnum, sendseqnum; 00052 char secret[DIGEST_MD5_LENGTH]; 00053 char kic[DIGEST_MD5_LENGTH]; 00054 char kcc[DIGEST_MD5_LENGTH]; 00055 char kis[DIGEST_MD5_LENGTH]; 00056 char kcs[DIGEST_MD5_LENGTH]; 00057 digest_md5_challenge challenge; 00058 digest_md5_response response; 00059 digest_md5_finish finish; 00060 }; 00061 typedef struct _Gsasl_digest_md5_client_state _Gsasl_digest_md5_client_state; 00062 00063 int 00064 _gsasl_digest_md5_client_start (Gsasl_session * sctx, void **mech_data) 00065 { 00066 _Gsasl_digest_md5_client_state *state; 00067 char nonce[CNONCE_ENTROPY_BYTES]; 00068 char *p; 00069 int rc; 00070 00071 rc = gsasl_nonce (nonce, CNONCE_ENTROPY_BYTES); 00072 if (rc != GSASL_OK) 00073 return rc; 00074 00075 rc = gsasl_base64_to (nonce, CNONCE_ENTROPY_BYTES, &p, NULL); 00076 if (rc != GSASL_OK) 00077 return rc; 00078 00079 state = calloc (1, sizeof (*state)); 00080 if (state == NULL) 00081 { 00082 free (p); 00083 return GSASL_MALLOC_ERROR; 00084 } 00085 00086 state->response.cnonce = p; 00087 state->response.nc = 1; 00088 00089 *mech_data = state; 00090 00091 return GSASL_OK; 00092 } 00093 00094 int 00095 _gsasl_digest_md5_client_step (Gsasl_session * sctx, 00096 void *mech_data, 00097 const char *input, 00098 size_t input_len, 00099 char **output, size_t * output_len) 00100 { 00101 _Gsasl_digest_md5_client_state *state = mech_data; 00102 int rc, res; 00103 00104 *output = NULL; 00105 *output_len = 0; 00106 00107 switch (state->step) 00108 { 00109 case 0: 00110 state->step++; 00111 if (input_len == 0) 00112 return GSASL_NEEDS_MORE; 00113 /* fall through */ 00114 00115 case 1: 00116 { 00117 if (digest_md5_parse_challenge (input, input_len, 00118 &state->challenge) < 0) 00119 return GSASL_MECHANISM_PARSE_ERROR; 00120 00121 /* FIXME: How to let application know of remaining realms? 00122 One idea, add a GSASL_REALM_COUNT property, and have the 00123 GSASL_REALM be that many concatenated zero terminated realm 00124 strings. Slightly hackish, though. Another cleaner 00125 approach would be to add gsasl_property_set_array and 00126 gsasl_property_get_array APIs, for those properties that 00127 may be used multiple times. */ 00128 if (state->challenge.nrealms > 0) 00129 gsasl_property_set (sctx, GSASL_REALM, state->challenge.realms[0]); 00130 else 00131 gsasl_property_set (sctx, GSASL_REALM, NULL); 00132 00133 /* FIXME: cipher, maxbuf. */ 00134 00135 /* Create response token. */ 00136 state->response.utf8 = 1; 00137 00138 gsasl_property_set (sctx, GSASL_QOPS, 00139 digest_md5_qops2qopstr (state->challenge.qops)); 00140 00141 { 00142 const char *qop = gsasl_property_get (sctx, GSASL_QOP); 00143 00144 if (!qop) 00145 state->response.qop = GSASL_QOP_AUTH; 00146 else if (strcmp (qop, "qop-int") == 0) 00147 state->response.qop = GSASL_QOP_AUTH_INT; 00148 else if (strcmp (qop, "qop-auth") == 0) 00149 state->response.qop = GSASL_QOP_AUTH; 00150 else 00151 /* We don't support confidentiality or unknown 00152 keywords. */ 00153 return GSASL_AUTHENTICATION_ERROR; 00154 } 00155 00156 state->response.nonce = strdup (state->challenge.nonce); 00157 if (!state->response.nonce) 00158 return GSASL_MALLOC_ERROR; 00159 00160 { 00161 const char *service = gsasl_property_get (sctx, GSASL_SERVICE); 00162 const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME); 00163 if (!service) 00164 return GSASL_NO_SERVICE; 00165 if (!hostname) 00166 return GSASL_NO_HOSTNAME; 00167 if (asprintf (&state->response.digesturi, "%s/%s", 00168 service, hostname) < 0) 00169 return GSASL_MALLOC_ERROR; 00170 } 00171 00172 { 00173 const char *c; 00174 char *tmp, *tmp2; 00175 00176 c = gsasl_property_get (sctx, GSASL_AUTHID); 00177 if (!c) 00178 return GSASL_NO_AUTHID; 00179 00180 state->response.username = strdup (c); 00181 if (!state->response.username) 00182 return GSASL_MALLOC_ERROR; 00183 00184 c = gsasl_property_get (sctx, GSASL_AUTHZID); 00185 if (c) 00186 { 00187 state->response.authzid = strdup (c); 00188 if (!state->response.authzid) 00189 return GSASL_MALLOC_ERROR; 00190 } 00191 00192 gsasl_callback (NULL, sctx, GSASL_REALM); 00193 c = gsasl_property_fast (sctx, GSASL_REALM); 00194 if (c) 00195 { 00196 state->response.realm = strdup (c); 00197 if (!state->response.realm) 00198 return GSASL_MALLOC_ERROR; 00199 } 00200 00201 c = gsasl_property_get (sctx, GSASL_PASSWORD); 00202 if (!c) 00203 return GSASL_NO_PASSWORD; 00204 00205 tmp2 = utf8tolatin1ifpossible (c); 00206 00207 rc = asprintf (&tmp, "%s:%s:%s", state->response.username, 00208 state->response.realm ? 00209 state->response.realm : "", tmp2); 00210 free (tmp2); 00211 if (rc < 0) 00212 return GSASL_MALLOC_ERROR; 00213 00214 rc = gsasl_md5 (tmp, strlen (tmp), &tmp2); 00215 free (tmp); 00216 if (rc != GSASL_OK) 00217 return rc; 00218 memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH); 00219 free (tmp2); 00220 } 00221 00222 rc = digest_md5_hmac (state->response.response, 00223 state->secret, 00224 state->response.nonce, 00225 state->response.nc, 00226 state->response.cnonce, 00227 state->response.qop, 00228 state->response.authzid, 00229 state->response.digesturi, 00230 0, 00231 state->response.cipher, 00232 state->kic, state->kis, state->kcc, state->kcs); 00233 if (rc) 00234 return GSASL_CRYPTO_ERROR; 00235 00236 *output = digest_md5_print_response (&state->response); 00237 if (!*output) 00238 return GSASL_AUTHENTICATION_ERROR; 00239 00240 *output_len = strlen (*output); 00241 00242 state->step++; 00243 res = GSASL_NEEDS_MORE; 00244 } 00245 break; 00246 00247 case 2: 00248 { 00249 char check[DIGEST_MD5_RESPONSE_LENGTH + 1]; 00250 00251 if (digest_md5_parse_finish (input, input_len, &state->finish) < 0) 00252 return GSASL_MECHANISM_PARSE_ERROR; 00253 00254 res = digest_md5_hmac (check, state->secret, 00255 state->response.nonce, state->response.nc, 00256 state->response.cnonce, state->response.qop, 00257 state->response.authzid, 00258 state->response.digesturi, 1, 00259 state->response.cipher, NULL, NULL, NULL, 00260 NULL); 00261 if (res != GSASL_OK) 00262 break; 00263 00264 if (strcmp (state->finish.rspauth, check) == 0) 00265 res = GSASL_OK; 00266 else 00267 res = GSASL_AUTHENTICATION_ERROR; 00268 state->step++; 00269 } 00270 break; 00271 00272 default: 00273 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; 00274 break; 00275 } 00276 00277 return res; 00278 } 00279 00280 void 00281 _gsasl_digest_md5_client_finish (Gsasl_session * sctx, void *mech_data) 00282 { 00283 _Gsasl_digest_md5_client_state *state = mech_data; 00284 00285 if (!state) 00286 return; 00287 00288 digest_md5_free_challenge (&state->challenge); 00289 digest_md5_free_response (&state->response); 00290 digest_md5_free_finish (&state->finish); 00291 00292 free (state); 00293 } 00294 00295 int 00296 _gsasl_digest_md5_client_encode (Gsasl_session * sctx, 00297 void *mech_data, 00298 const char *input, 00299 size_t input_len, 00300 char **output, size_t * output_len) 00301 { 00302 _Gsasl_digest_md5_client_state *state = mech_data; 00303 int res; 00304 00305 res = digest_md5_encode (input, input_len, output, output_len, 00306 state->response.qop, 00307 state->sendseqnum, state->kic); 00308 if (res) 00309 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR; 00310 00311 if (state->sendseqnum == 4294967295UL) 00312 state->sendseqnum = 0; 00313 else 00314 state->sendseqnum++; 00315 00316 return GSASL_OK; 00317 } 00318 00319 int 00320 _gsasl_digest_md5_client_decode (Gsasl_session * sctx, 00321 void *mech_data, 00322 const char *input, 00323 size_t input_len, 00324 char **output, size_t * output_len) 00325 { 00326 _Gsasl_digest_md5_client_state *state = mech_data; 00327 int res; 00328 00329 res = digest_md5_decode (input, input_len, output, output_len, 00330 state->response.qop, 00331 state->readseqnum, state->kis); 00332 if (res) 00333 return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR; 00334 00335 if (state->readseqnum == 4294967295UL) 00336 state->readseqnum = 0; 00337 else 00338 state->readseqnum++; 00339 00340 return GSASL_OK; 00341 }
1.7.6.1