digest-md5/client.c

Go to the documentation of this file.
00001 /* client.c --- DIGEST-MD5 mechanism from RFC 2831, client side.
00002  * Copyright (C) 2002, 2003, 2004, 2006, 2008  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 
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       /* fall through */
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         /* FIXME: How to let application know of remaining realms?
00121            One idea, add a GSASL_REALM_COUNT property, and have the
00122            GSASL_REALM be that many concatenated zero terminated realm
00123            strings.  Slightly hackish, though.  Another cleaner
00124            approach would be to add gsasl_property_set_array and
00125            gsasl_property_get_array APIs, for those properties that
00126            may be used multiple times. */
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         /* FIXME: qop, cipher, maxbuf. */
00133 
00134         /* Create response token. */
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 }

Generated on Tue Oct 21 18:28:18 2008 for gsasl by  doxygen 1.5.6