gsasl  1.8.0
kerberos_v5/client.c
Go to the documentation of this file.
00001 /* client.c --- Experimental SASL mechanism KERBEROS_V5, client side.
00002  * Copyright (C) 2003-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  * NB!  Shishi is licensed under GPL, so linking GSASL with it require
00022  * that you follow the GPL for GSASL as well.
00023  *
00024  */
00025 
00026 #include "kerberos_v5.h"
00027 
00028 #include "shared.h"
00029 
00030 struct _Gsasl_kerberos_v5_client_state
00031 {
00032   int step;
00033   char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
00034   int serverqops;
00035   int clientqop;
00036   int servermutual;
00037   uint32_t servermaxbuf;
00038   uint32_t clientmaxbuf;
00039   Shishi *sh;
00040   Shishi_tkt *tkt;
00041   Shishi_as *as;
00042   Shishi_ap *ap;
00043   Shishi_key *sessionkey;
00044   Shishi_safe *safe;
00045 };
00046 
00047 int
00048 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx)
00049 {
00050   if (!shishi_check_version (SHISHI_VERSION))
00051     return GSASL_UNKNOWN_MECHANISM;
00052 
00053   return GSASL_OK;
00054 }
00055 
00056 int
00057 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data)
00058 {
00059   struct _Gsasl_kerberos_v5_client_state *state;
00060   Gsasl_ctx *ctx;
00061   int err;
00062 
00063   state = malloc (sizeof (*state));
00064   if (state == NULL)
00065     return GSASL_MALLOC_ERROR;
00066 
00067   memset (state, 0, sizeof (*state));
00068 
00069   err = shishi_init (&state->sh);
00070   if (err)
00071     return GSASL_KERBEROS_V5_INIT_ERROR;
00072 
00073   state->step = 0;
00074   state->clientqop = GSASL_QOP_AUTH_INT;
00075 
00076   *mech_data = state;
00077 
00078   return GSASL_OK;
00079 }
00080 
00081 #define STEP_FIRST 0
00082 #define STEP_NONINFRA_SEND_ASREQ 1
00083 #define STEP_NONINFRA_WAIT_ASREP 2
00084 #define STEP_NONINFRA_SEND_APREQ 3
00085 #define STEP_NONINFRA_WAIT_APREP 4
00086 #define STEP_SUCCESS 5
00087 
00088 int
00089 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx,
00090                                 void *mech_data,
00091                                 const char *input,
00092                                 size_t input_len,
00093                                 char *output, size_t * output_len)
00094 {
00095   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00096   Gsasl_client_callback_authentication_id cb_authentication_id;
00097   Gsasl_client_callback_authorization_id cb_authorization_id;
00098   Gsasl_client_callback_qop cb_qop;
00099   Gsasl_client_callback_realm cb_realm;
00100   Gsasl_client_callback_password cb_password;
00101   Gsasl_client_callback_service cb_service;
00102   Gsasl_client_callback_maxbuf cb_maxbuf;
00103   Gsasl_ctx *ctx;
00104   int res;
00105   int len;
00106 
00107   ctx = gsasl_client_ctx_get (sctx);
00108   if (ctx == NULL)
00109     return GSASL_CANNOT_GET_CTX;
00110 
00111   /* These are optional */
00112   cb_realm = gsasl_client_callback_realm_get (ctx);
00113   cb_service = gsasl_client_callback_service_get (ctx);
00114   cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
00115   cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx);
00116   cb_qop = gsasl_client_callback_qop_get (ctx);
00117   cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx);
00118 
00119   /* Only optionally needed in infrastructure mode */
00120   cb_password = gsasl_client_callback_password_get (ctx);
00121   if (cb_password == NULL)
00122     return GSASL_NEED_CLIENT_PASSWORD_CALLBACK;
00123 
00124   /* I think we really need this one */
00125   cb_service = gsasl_client_callback_service_get (ctx);
00126   if (cb_service == NULL)
00127     return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
00128 
00129   switch (state->step)
00130     {
00131     case STEP_FIRST:
00132       if (input == NULL)
00133         {
00134           *output_len = 0;
00135           return GSASL_NEEDS_MORE;
00136         }
00137 
00138       if (input_len != SERVER_HELLO_LEN)
00139         return GSASL_MECHANISM_PARSE_ERROR;
00140 
00141       memcpy (state->serverhello, input, input_len);
00142 
00143       {
00144         unsigned char serverbitmap;
00145 
00146         memcpy (&serverbitmap, input, BITMAP_LEN);
00147         state->serverqops = 0;
00148         if (serverbitmap & GSASL_QOP_AUTH)
00149           state->serverqops |= GSASL_QOP_AUTH;
00150         if (serverbitmap & GSASL_QOP_AUTH_INT)
00151           state->serverqops |= GSASL_QOP_AUTH_INT;
00152         if (serverbitmap & GSASL_QOP_AUTH_CONF)
00153           state->serverqops |= GSASL_QOP_AUTH_CONF;
00154         if (serverbitmap & MUTUAL)
00155           state->servermutual = 1;
00156       }
00157       memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
00158       state->servermaxbuf = ntohl (state->servermaxbuf);
00159 
00160       if (cb_qop)
00161         state->clientqop = cb_qop (sctx, state->serverqops);
00162 
00163       if (!(state->serverqops & state->clientqop &
00164             (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF)))
00165         return GSASL_AUTHENTICATION_ERROR;
00166 
00167       /* XXX for now we require server authentication */
00168       if (!state->servermutual)
00169         return GSASL_AUTHENTICATION_ERROR;
00170 
00171       /* Decide policy here: non-infrastructure, infrastructure or proxy.
00172        *
00173        * A callback to decide should be added, but without the default
00174        * should be:
00175        *
00176        * IF shishi_tktset_get_for_server() THEN
00177        *    INFRASTRUCTURE MODE
00178        * ELSE IF shishi_realm_for_server(server) THEN
00179        *    PROXY INFRASTRUCTURE (then fallback to NIM?)
00180        * ELSE
00181        *    NON-INFRASTRUCTURE MODE
00182        */
00183       state->step = STEP_NONINFRA_SEND_APREQ;   /* only NIM for now.. */
00184       /* fall through */
00185 
00186     case STEP_NONINFRA_SEND_ASREQ:
00187       res = shishi_as (state->sh, &state->as);
00188       if (res)
00189         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00190 
00191       if (cb_authentication_id) /* Shishi defaults to one otherwise */
00192         {
00193           len = *output_len - 1;
00194           res = cb_authentication_id (sctx, output, &len);
00195           if (res != GSASL_OK)
00196             return res;
00197           output[len] = '\0';
00198 
00199           res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as),
00200                                          SHISHI_NT_UNKNOWN, output);
00201           if (res != GSASL_OK)
00202             return res;
00203         }
00204 
00205       if (cb_realm)
00206         {
00207           len = *output_len - 1;
00208           res = cb_realm (sctx, output, &len);
00209           if (res != GSASL_OK)
00210             return res;
00211         }
00212       else
00213         len = 0;
00214 
00215       output[len] = '\0';
00216       res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as),
00217                                      output);
00218       if (res != GSASL_OK)
00219         return res;
00220 
00221       if (cb_service)
00222         {
00223           char *sname[3];
00224           size_t servicelen = 0;
00225           size_t hostnamelen = 0;
00226 
00227           res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen,
00228                             /* XXX support servicename a'la DIGEST-MD5 too? */
00229                             NULL, NULL);
00230           if (res != GSASL_OK)
00231             return res;
00232 
00233           if (*output_len < servicelen + 1 + hostnamelen + 1)
00234             return GSASL_TOO_SMALL_BUFFER;
00235 
00236           sname[0] = &output[0];
00237           sname[1] = &output[servicelen + 2];
00238           sname[2] = NULL;
00239 
00240           res = cb_service (sctx, sname[0], &servicelen,
00241                             sname[1], &hostnamelen, NULL, NULL);
00242           if (res != GSASL_OK)
00243             return res;
00244 
00245           sname[0][servicelen] = '\0';
00246           sname[1][hostnamelen] = '\0';
00247 
00248           res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as),
00249                                          SHISHI_NT_UNKNOWN, sname);
00250           if (res != GSASL_OK)
00251             return res;
00252         }
00253 
00254       /* XXX query application for encryption types and set the etype
00255          field?  Already configured by shishi though... */
00256 
00257       res = shishi_a2d (state->sh, shishi_as_req (state->as),
00258                         output, output_len);
00259       if (res != SHISHI_OK)
00260         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00261 
00262       state->step = STEP_NONINFRA_WAIT_ASREP;
00263 
00264       res = GSASL_NEEDS_MORE;
00265       break;
00266 
00267     case STEP_NONINFRA_WAIT_ASREP:
00268       if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK)
00269         return GSASL_MECHANISM_PARSE_ERROR;
00270 
00271       /* XXX? password stored in callee's output buffer */
00272       len = *output_len - 1;
00273       res = cb_password (sctx, output, &len);
00274       if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
00275         return res;
00276       output[len] = '\0';
00277 
00278       res = shishi_as_rep_process (state->as, NULL, output);
00279       if (res != SHISHI_OK)
00280         return GSASL_AUTHENTICATION_ERROR;
00281 
00282       state->step = STEP_NONINFRA_SEND_APREQ;
00283       /* fall through */
00284 
00285     case STEP_NONINFRA_SEND_APREQ:
00286       if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
00287         return GSASL_TOO_SMALL_BUFFER;
00288 
00289       if (!(state->clientqop & ~GSASL_QOP_AUTH))
00290         state->clientmaxbuf = 0;
00291       else if (cb_maxbuf)
00292         state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf);
00293       else
00294         state->clientmaxbuf = MAXBUF_DEFAULT;
00295 
00296       /* XXX for now we require server authentication */
00297       output[0] = state->clientqop | MUTUAL;
00298       {
00299         uint32_t tmp;
00300 
00301         tmp = ntohl (state->clientmaxbuf);
00302         memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN);
00303       }
00304       memcpy (&output[CLIENT_HELLO_LEN], state->serverhello,
00305               SERVER_HELLO_LEN);
00306 
00307       if (cb_authorization_id)
00308         {
00309           len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
00310           res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN +
00311                                                    SERVER_HELLO_LEN], &len);
00312         }
00313       else
00314         len = 0;
00315 
00316       len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
00317       res = shishi_ap_tktoptionsdata (state->sh,
00318                                       &state->ap,
00319                                       shishi_as_tkt (state->as),
00320                                       SHISHI_APOPTIONS_MUTUAL_REQUIRED,
00321                                       output, len);
00322       if (res != SHISHI_OK)
00323         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00324 
00325       res = shishi_authenticator_add_authorizationdata
00326         (state->sh, shishi_ap_authenticator (state->ap), -1, output, len);
00327       if (res != SHISHI_OK)
00328         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00329 
00330       /* XXX set realm in AP-REQ and Authenticator */
00331 
00332       res = shishi_ap_req_der (state->ap, output, output_len);
00333       if (res != SHISHI_OK)
00334         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00335 
00336       state->step = STEP_NONINFRA_WAIT_APREP;
00337 
00338       res = GSASL_NEEDS_MORE;
00339       break;
00340 
00341     case STEP_NONINFRA_WAIT_APREP:
00342       if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK)
00343         return GSASL_MECHANISM_PARSE_ERROR;
00344 
00345       res = shishi_ap_rep_verify (state->ap);
00346       if (res != SHISHI_OK)
00347         return GSASL_AUTHENTICATION_ERROR;
00348 
00349       state->step = STEP_SUCCESS;
00350 
00351       /* XXX support AP session keys */
00352       state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as));
00353 
00354       *output_len = 0;
00355       res = GSASL_OK;
00356       break;
00357 
00358     default:
00359       res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00360       break;
00361     }
00362 
00363   return res;
00364 }
00365 
00366 int
00367 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx,
00368                                   void *mech_data,
00369                                   const char *input,
00370                                   size_t input_len,
00371                                   char **output, size_t * output_len)
00372 {
00373   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00374   int res;
00375 
00376   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00377     {
00378       return GSASL_INTEGRITY_ERROR;
00379     }
00380   else if (state && state->sessionkey
00381            && state->clientqop & GSASL_QOP_AUTH_INT)
00382     {
00383       res = shishi_safe (state->sh, &state->safe);
00384       if (res != SHISHI_OK)
00385         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00386 
00387       res = shishi_safe_set_user_data (state->sh,
00388                                        shishi_safe_safe (state->safe),
00389                                        input, input_len);
00390       if (res != SHISHI_OK)
00391         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00392 
00393       res = shishi_safe_build (state->safe, state->sessionkey);
00394       if (res != SHISHI_OK)
00395         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00396 
00397       res = shishi_safe_safe_der (state->safe, output, output_len);
00398       if (res != SHISHI_OK)
00399         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00400     }
00401   else
00402     {
00403       *output_len = input_len;
00404       *output = malloc (input_len);
00405       if (!*output)
00406         return GSASL_MALLOC_ERROR;
00407       memcpy (*output, input, input_len);
00408     }
00409 
00410   return GSASL_OK;
00411 }
00412 
00413 int
00414 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx,
00415                                   void *mech_data,
00416                                   const char *input,
00417                                   size_t input_len,
00418                                   char *output, size_t * output_len)
00419 {
00420   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00421 
00422   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00423     {
00424       return GSASL_INTEGRITY_ERROR;
00425     }
00426   else if (state && state->sessionkey
00427            && state->clientqop & GSASL_QOP_AUTH_INT)
00428     {
00429       return GSASL_INTEGRITY_ERROR;
00430     }
00431   else
00432     {
00433       *output_len = input_len;
00434       *output = malloc (input_len);
00435       if (!*output)
00436         return GSASL_MALLOC_ERROR;
00437       memcpy (*output, input, input_len);
00438     }
00439 
00440   return GSASL_OK;
00441 }
00442 
00443 int
00444 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data)
00445 {
00446   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00447 
00448   shishi_done (state->sh);
00449   free (state);
00450 
00451   return GSASL_OK;
00452 }