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