digest-md5/server.c

Go to the documentation of this file.
00001 /* server.c --- DIGEST-MD5 mechanism from RFC 2831, server 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, strdup, 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 #include "validate.h"
00044 
00045 #define NONCE_ENTROPY_BYTES 16
00046 
00047 struct _Gsasl_digest_md5_server_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_server_state _Gsasl_digest_md5_server_state;
00061 
00062 int
00063 _gsasl_digest_md5_server_start (Gsasl_session * sctx, void **mech_data)
00064 {
00065   _Gsasl_digest_md5_server_state *state;
00066   char nonce[NONCE_ENTROPY_BYTES];
00067   char *p;
00068   int rc;
00069 
00070   rc = gsasl_nonce (nonce, NONCE_ENTROPY_BYTES);
00071   if (rc != GSASL_OK)
00072     return rc;
00073 
00074   rc = gsasl_base64_to (nonce, NONCE_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->challenge.qops = DIGEST_MD5_QOP_AUTH | DIGEST_MD5_QOP_AUTH_INT;
00086   state->challenge.ciphers = 0;
00087 
00088   state->challenge.nonce = p;
00089   state->challenge.utf8 = 1;
00090 
00091   *mech_data = state;
00092 
00093   return GSASL_OK;
00094 }
00095 
00096 int
00097 _gsasl_digest_md5_server_step (Gsasl_session * sctx,
00098                                void *mech_data,
00099                                const char *input,
00100                                size_t input_len,
00101                                char **output, size_t * output_len)
00102 {
00103   _Gsasl_digest_md5_server_state *state = mech_data;
00104   int rc, res;
00105 
00106   *output = NULL;
00107   *output_len = 0;
00108 
00109   switch (state->step)
00110     {
00111     case 0:
00112       /* Set realm. */
00113       {
00114         const char *c;
00115         c = gsasl_property_get (sctx, GSASL_REALM);
00116         if (c)
00117           {
00118             state->challenge.nrealms = 1;
00119 
00120             state->challenge.realms =
00121               malloc (sizeof (*state->challenge.realms));
00122             if (!state->challenge.realms)
00123               return GSASL_MALLOC_ERROR;
00124 
00125             state->challenge.realms[0] = strdup (c);
00126             if (!state->challenge.realms[0])
00127               return GSASL_MALLOC_ERROR;
00128           }
00129       }
00130 
00131       /* FIXME: qop, cipher, maxbuf, more realms. */
00132 
00133       /* Create challenge. */
00134       *output = digest_md5_print_challenge (&state->challenge);
00135       if (!*output)
00136         return GSASL_AUTHENTICATION_ERROR;
00137 
00138       *output_len = strlen (*output);
00139       state->step++;
00140       res = GSASL_NEEDS_MORE;
00141       break;
00142 
00143     case 1:
00144       if (digest_md5_parse_response (input, input_len, &state->response) < 0)
00145         return GSASL_MECHANISM_PARSE_ERROR;
00146 
00147       /* Make sure response is consistent with challenge. */
00148       if (digest_md5_validate (&state->challenge, &state->response) < 0)
00149         return GSASL_MECHANISM_PARSE_ERROR;
00150 
00151       /* Store properties, from the client response. */
00152       gsasl_property_set (sctx, GSASL_AUTHID, state->response.username);
00153       gsasl_property_set (sctx, GSASL_AUTHZID, state->response.authzid);
00154       gsasl_property_set (sctx, GSASL_REALM, state->response.realm);
00155 
00156       /* FIXME: qop, cipher, maxbuf. */
00157 
00158       /* Compute secret.  TODO: Add callback to retrieve hashed
00159          secret. */
00160       {
00161         const char *c;
00162         char *tmp, *tmp2;
00163 
00164         c = gsasl_property_get (sctx, GSASL_PASSWORD);
00165         if (!c)
00166           return GSASL_NO_PASSWORD;
00167 
00168         if (asprintf (&tmp, "%s:%s:%s", state->response.username,
00169                       state->response.realm ?
00170                       state->response.realm : "", c) < 0)
00171           return GSASL_MALLOC_ERROR;
00172 
00173         rc = gsasl_md5 (tmp, strlen (tmp), &tmp2);
00174         free (tmp);
00175         if (rc != GSASL_OK)
00176           return rc;
00177         memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH);
00178         free (tmp2);
00179       }
00180 
00181       /* Check client response. */
00182       {
00183         char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
00184 
00185         rc = digest_md5_hmac (check, state->secret,
00186                               state->response.nonce, state->response.nc,
00187                               state->response.cnonce, state->response.qop,
00188                               state->response.authzid,
00189                               state->response.digesturi, 0,
00190                               state->response.cipher, NULL, NULL, NULL, NULL);
00191         if (rc)
00192           return GSASL_AUTHENTICATION_ERROR;
00193 
00194         if (strcmp (state->response.response, check) != 0)
00195           return GSASL_AUTHENTICATION_ERROR;
00196       }
00197 
00198       /* Create finish token. */
00199       rc = digest_md5_hmac (state->finish.rspauth, state->secret,
00200                             state->response.nonce, state->response.nc,
00201                             state->response.cnonce, state->response.qop,
00202                             state->response.authzid,
00203                             state->response.digesturi, 1,
00204                             state->response.cipher, NULL, NULL, NULL, NULL);
00205       if (rc)
00206         return GSASL_AUTHENTICATION_ERROR;
00207 
00208       *output = digest_md5_print_finish (&state->finish);
00209       if (!*output)
00210         return GSASL_MALLOC_ERROR;
00211 
00212       *output_len = strlen (*output);
00213 
00214       state->step++;
00215       res = GSASL_NEEDS_MORE;
00216       break;
00217 
00218     case 2:
00219       *output_len = 0;
00220       state->step++;
00221       res = GSASL_OK;
00222       break;
00223 
00224     default:
00225       res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00226       break;
00227     }
00228 
00229   return res;
00230 }
00231 
00232 void
00233 _gsasl_digest_md5_server_finish (Gsasl_session * sctx, void *mech_data)
00234 {
00235   _Gsasl_digest_md5_server_state *state = mech_data;
00236 
00237   if (!state)
00238     return;
00239 
00240   digest_md5_free_challenge (&state->challenge);
00241   digest_md5_free_response (&state->response);
00242   digest_md5_free_finish (&state->finish);
00243 
00244   free (state);
00245 }
00246 
00247 int
00248 _gsasl_digest_md5_server_encode (Gsasl_session * sctx,
00249                                  void *mech_data,
00250                                  const char *input,
00251                                  size_t input_len,
00252                                  char **output, size_t * output_len)
00253 {
00254   _Gsasl_digest_md5_server_state *state = mech_data;
00255   int res;
00256 
00257   res = digest_md5_encode (input, input_len, output, output_len,
00258                            state->response.qop, state->sendseqnum,
00259                            state->kis);
00260   if (res)
00261     return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00262 
00263   if (state->sendseqnum == 4294967295UL)
00264     state->sendseqnum = 0;
00265   else
00266     state->sendseqnum++;
00267 
00268   return GSASL_OK;
00269 }
00270 
00271 int
00272 _gsasl_digest_md5_server_decode (Gsasl_session * sctx,
00273                                  void *mech_data,
00274                                  const char *input,
00275                                  size_t input_len,
00276                                  char **output, size_t * output_len)
00277 {
00278   _Gsasl_digest_md5_server_state *state = mech_data;
00279   int res;
00280 
00281   res = digest_md5_decode (input, input_len, output, output_len,
00282                            state->response.qop, state->readseqnum,
00283                            state->kic);
00284   if (res)
00285     return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00286 
00287   if (state->readseqnum == 4294967295UL)
00288     state->readseqnum = 0;
00289   else
00290     state->readseqnum++;
00291 
00292   return GSASL_OK;
00293 }

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