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, 2007, 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, strdup, 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 "validate.h"
00045 
00046 #define NONCE_ENTROPY_BYTES 16
00047 
00048 struct _Gsasl_digest_md5_server_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_server_state _Gsasl_digest_md5_server_state;
00062 
00063 int
00064 _gsasl_digest_md5_server_start (Gsasl_session * sctx, void **mech_data)
00065 {
00066   _Gsasl_digest_md5_server_state *state;
00067   char nonce[NONCE_ENTROPY_BYTES];
00068   char *p;
00069   int rc;
00070 
00071   rc = gsasl_nonce (nonce, NONCE_ENTROPY_BYTES);
00072   if (rc != GSASL_OK)
00073     return rc;
00074 
00075   rc = gsasl_base64_to (nonce, NONCE_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->challenge.qops = DIGEST_MD5_QOP_AUTH | DIGEST_MD5_QOP_AUTH_INT;
00087   state->challenge.ciphers = 0;
00088 
00089   state->challenge.nonce = p;
00090   state->challenge.utf8 = 1;
00091 
00092   *mech_data = state;
00093 
00094   return GSASL_OK;
00095 }
00096 
00097 static char
00098 _gsasl_digest_md5_hexdigit_to_char (char hexdigit)
00099 {
00100   /* The hex representation always contains lowercase alphabetic
00101      characters.  See RFC 2831, 1.1. */
00102 
00103   if (hexdigit >= '0' && hexdigit <= '9')
00104     return hexdigit - '0';
00105   if (hexdigit >= 'a' && hexdigit <= 'z')
00106     return hexdigit - 'a' + 10;
00107 
00108   return -1;
00109 }
00110 
00111 static char
00112 _gsasl_digest_md5_hex_to_char (char u, char l)
00113 {
00114   return (char) (((unsigned char) _gsasl_digest_md5_hexdigit_to_char (u)) *
00115                  16 + _gsasl_digest_md5_hexdigit_to_char (l));
00116 }
00117 
00118 static int
00119 _gsasl_digest_md5_set_hashed_secret (char *secret, const char *hex_secret)
00120 {
00121   /* Convert the hex string containing the secret to a byte array */
00122   const char *p;
00123   char *s;
00124 
00125   if (!hex_secret)
00126     return GSASL_AUTHENTICATION_ERROR;
00127 
00128   s = secret;
00129   p = hex_secret;
00130   while (*p)
00131     {
00132       *s = _gsasl_digest_md5_hex_to_char (p[0], p[1]);
00133       s++;
00134 
00135       p += 2;
00136     }
00137 
00138   return GSASL_OK;
00139 }
00140 
00141 int
00142 _gsasl_digest_md5_server_step (Gsasl_session * sctx,
00143                                void *mech_data,
00144                                const char *input,
00145                                size_t input_len,
00146                                char **output, size_t * output_len)
00147 {
00148   _Gsasl_digest_md5_server_state *state = mech_data;
00149   int rc, res;
00150 
00151   *output = NULL;
00152   *output_len = 0;
00153 
00154   switch (state->step)
00155     {
00156     case 0:
00157       /* Set realm. */
00158       {
00159         const char *c;
00160         c = gsasl_property_get (sctx, GSASL_REALM);
00161         if (c)
00162           {
00163             state->challenge.nrealms = 1;
00164 
00165             state->challenge.realms =
00166               malloc (sizeof (*state->challenge.realms));
00167             if (!state->challenge.realms)
00168               return GSASL_MALLOC_ERROR;
00169 
00170             state->challenge.realms[0] = strdup (c);
00171             if (!state->challenge.realms[0])
00172               return GSASL_MALLOC_ERROR;
00173           }
00174       }
00175 
00176       /* FIXME: qop, cipher, maxbuf, more realms. */
00177 
00178       /* Create challenge. */
00179       *output = digest_md5_print_challenge (&state->challenge);
00180       if (!*output)
00181         return GSASL_AUTHENTICATION_ERROR;
00182 
00183       *output_len = strlen (*output);
00184       state->step++;
00185       res = GSASL_NEEDS_MORE;
00186       break;
00187 
00188     case 1:
00189       if (digest_md5_parse_response (input, input_len, &state->response) < 0)
00190         return GSASL_MECHANISM_PARSE_ERROR;
00191 
00192       /* Make sure response is consistent with challenge. */
00193       if (digest_md5_validate (&state->challenge, &state->response) < 0)
00194         return GSASL_MECHANISM_PARSE_ERROR;
00195 
00196       /* Store properties, from the client response. */
00197       if (state->response.utf8)
00198         {
00199           gsasl_property_set (sctx, GSASL_AUTHID, state->response.username);
00200           gsasl_property_set (sctx, GSASL_REALM, state->response.realm);
00201         }
00202       else
00203         {
00204           /* Client provided username/realm in ISO-8859-1 form,
00205              convert it to UTF-8 since the library is all-UTF-8. */
00206           char *tmp;
00207 
00208           tmp = latin1toutf8 (state->response.username);
00209           if (!tmp)
00210             return GSASL_MALLOC_ERROR;
00211           gsasl_property_set (sctx, GSASL_AUTHID, tmp);
00212           free (tmp);
00213 
00214           tmp = latin1toutf8 (state->response.realm);
00215           if (!tmp)
00216             return GSASL_MALLOC_ERROR;
00217           gsasl_property_set (sctx, GSASL_REALM, tmp);
00218           free (tmp);
00219         }
00220       gsasl_property_set (sctx, GSASL_AUTHZID, state->response.authzid);
00221 
00222       /* FIXME: qop, cipher, maxbuf.  */
00223 
00224       /* Compute secret. */
00225       {
00226         const char *passwd;
00227         const char *hashed_passwd;
00228 
00229         hashed_passwd =
00230           gsasl_property_get (sctx, GSASL_DIGEST_MD5_HASHED_PASSWORD);
00231         if (hashed_passwd)
00232           {
00233             if (strlen (hashed_passwd) != (DIGEST_MD5_LENGTH * 2))
00234               return GSASL_AUTHENTICATION_ERROR;
00235 
00236             rc = _gsasl_digest_md5_set_hashed_secret (state->secret,
00237                                                       hashed_passwd);
00238             if (rc != GSASL_OK)
00239               return rc;
00240           }
00241         else if ((passwd = gsasl_property_get (sctx, GSASL_PASSWORD)) != NULL)
00242           {
00243             char *tmp, *tmp2;
00244 
00245             tmp2 = utf8tolatin1ifpossible (passwd);
00246 
00247             rc = asprintf (&tmp, "%s:%s:%s", state->response.username,
00248                            state->response.realm ?
00249                            state->response.realm : "", tmp2);
00250             free (tmp2);
00251             if (rc < 0)
00252               return GSASL_MALLOC_ERROR;
00253 
00254             rc = gsasl_md5 (tmp, strlen (tmp), &tmp2);
00255             free (tmp);
00256             if (rc != GSASL_OK)
00257               return rc;
00258 
00259             memcpy (state->secret, tmp2, DIGEST_MD5_LENGTH);
00260             free (tmp2);
00261           }
00262         else
00263           {
00264             return GSASL_NO_PASSWORD;
00265           }
00266       }
00267 
00268       /* Check client response. */
00269       {
00270         char check[DIGEST_MD5_RESPONSE_LENGTH + 1];
00271 
00272         rc = digest_md5_hmac (check, state->secret,
00273                               state->response.nonce, state->response.nc,
00274                               state->response.cnonce, state->response.qop,
00275                               state->response.authzid,
00276                               state->response.digesturi, 0,
00277                               state->response.cipher, NULL, NULL, NULL, NULL);
00278         if (rc)
00279           return GSASL_AUTHENTICATION_ERROR;
00280 
00281         if (strcmp (state->response.response, check) != 0)
00282           return GSASL_AUTHENTICATION_ERROR;
00283       }
00284 
00285       /* Create finish token. */
00286       rc = digest_md5_hmac (state->finish.rspauth, state->secret,
00287                             state->response.nonce, state->response.nc,
00288                             state->response.cnonce, state->response.qop,
00289                             state->response.authzid,
00290                             state->response.digesturi, 1,
00291                             state->response.cipher, NULL, NULL, NULL, NULL);
00292       if (rc)
00293         return GSASL_AUTHENTICATION_ERROR;
00294 
00295       *output = digest_md5_print_finish (&state->finish);
00296       if (!*output)
00297         return GSASL_MALLOC_ERROR;
00298 
00299       *output_len = strlen (*output);
00300 
00301       state->step++;
00302       res = GSASL_NEEDS_MORE;
00303       break;
00304 
00305     case 2:
00306       *output_len = 0;
00307       state->step++;
00308       res = GSASL_OK;
00309       break;
00310 
00311     default:
00312       res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00313       break;
00314     }
00315 
00316   return res;
00317 }
00318 
00319 void
00320 _gsasl_digest_md5_server_finish (Gsasl_session * sctx, void *mech_data)
00321 {
00322   _Gsasl_digest_md5_server_state *state = mech_data;
00323 
00324   if (!state)
00325     return;
00326 
00327   digest_md5_free_challenge (&state->challenge);
00328   digest_md5_free_response (&state->response);
00329   digest_md5_free_finish (&state->finish);
00330 
00331   free (state);
00332 }
00333 
00334 int
00335 _gsasl_digest_md5_server_encode (Gsasl_session * sctx,
00336                                  void *mech_data,
00337                                  const char *input,
00338                                  size_t input_len,
00339                                  char **output, size_t * output_len)
00340 {
00341   _Gsasl_digest_md5_server_state *state = mech_data;
00342   int res;
00343 
00344   res = digest_md5_encode (input, input_len, output, output_len,
00345                            state->response.qop, state->sendseqnum,
00346                            state->kis);
00347   if (res)
00348     return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00349 
00350   if (state->sendseqnum == 4294967295UL)
00351     state->sendseqnum = 0;
00352   else
00353     state->sendseqnum++;
00354 
00355   return GSASL_OK;
00356 }
00357 
00358 int
00359 _gsasl_digest_md5_server_decode (Gsasl_session * sctx,
00360                                  void *mech_data,
00361                                  const char *input,
00362                                  size_t input_len,
00363                                  char **output, size_t * output_len)
00364 {
00365   _Gsasl_digest_md5_server_state *state = mech_data;
00366   int res;
00367 
00368   res = digest_md5_decode (input, input_len, output, output_len,
00369                            state->response.qop, state->readseqnum,
00370                            state->kic);
00371   if (res)
00372     return res == -2 ? GSASL_NEEDS_MORE : GSASL_INTEGRITY_ERROR;
00373 
00374   if (state->readseqnum == 4294967295UL)
00375     state->readseqnum = 0;
00376   else
00377     state->readseqnum++;
00378 
00379   return GSASL_OK;
00380 }

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