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

Generated on Tue Aug 22 12:06:06 2006 for gsasl by  doxygen 1.4.7