gsasl  1.8.0
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-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 }