gsasl  1.8.0
kerberos_v5.c
Go to the documentation of this file.
00001 /* kerberos_v5.c --- Implementation of experimental SASL mechanism KERBEROS_V5.
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 /* Get Shishi API. */
00029 #include <shishi.h>
00030 
00031 #ifdef HAVE_NETINET_IN_H
00032 #include <netinet/in.h>         /* ntohl */
00033 #endif
00034 
00035 #define DEBUG 0
00036 
00037 #define BITMAP_LEN 1
00038 #define MAXBUF_LEN 4
00039 #define RANDOM_LEN 16
00040 #define MUTUAL (1 << 3)
00041 
00042 #define SERVER_HELLO_LEN BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN
00043 #define CLIENT_HELLO_LEN BITMAP_LEN + MAXBUF_LEN
00044 
00045 #define MAXBUF_DEFAULT 65536
00046 
00047 /* Client */
00048 
00049 #ifdef USE_CLIENT
00050 
00051 struct _Gsasl_kerberos_v5_client_state
00052 {
00053   int step;
00054   char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
00055   int serverqops;
00056   int clientqop;
00057   int servermutual;
00058   uint32_t servermaxbuf;
00059   uint32_t clientmaxbuf;
00060   Shishi *sh;
00061   Shishi_tkt *tkt;
00062   Shishi_as *as;
00063   Shishi_ap *ap;
00064   Shishi_key *sessionkey;
00065   Shishi_safe *safe;
00066 };
00067 
00068 int
00069 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx)
00070 {
00071   if (!shishi_check_version (SHISHI_VERSION))
00072     return GSASL_UNKNOWN_MECHANISM;
00073 
00074   return GSASL_OK;
00075 }
00076 
00077 int
00078 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data)
00079 {
00080   struct _Gsasl_kerberos_v5_client_state *state;
00081   Gsasl_ctx *ctx;
00082   int err;
00083 
00084   state = malloc (sizeof (*state));
00085   if (state == NULL)
00086     return GSASL_MALLOC_ERROR;
00087 
00088   memset (state, 0, sizeof (*state));
00089 
00090   err = shishi_init (&state->sh);
00091   if (err)
00092     return GSASL_KERBEROS_V5_INIT_ERROR;
00093 
00094   state->step = 0;
00095   state->clientqop = GSASL_QOP_AUTH_INT;
00096 
00097   *mech_data = state;
00098 
00099   return GSASL_OK;
00100 }
00101 
00102 #define STEP_FIRST 0
00103 #define STEP_NONINFRA_SEND_ASREQ 1
00104 #define STEP_NONINFRA_WAIT_ASREP 2
00105 #define STEP_NONINFRA_SEND_APREQ 3
00106 #define STEP_NONINFRA_WAIT_APREP 4
00107 #define STEP_SUCCESS 5
00108 
00109 int
00110 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx,
00111                                 void *mech_data,
00112                                 const char *input,
00113                                 size_t input_len,
00114                                 char *output, size_t * output_len)
00115 {
00116   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00117   Gsasl_client_callback_authentication_id cb_authentication_id;
00118   Gsasl_client_callback_authorization_id cb_authorization_id;
00119   Gsasl_client_callback_qop cb_qop;
00120   Gsasl_client_callback_realm cb_realm;
00121   Gsasl_client_callback_password cb_password;
00122   Gsasl_client_callback_service cb_service;
00123   Gsasl_client_callback_maxbuf cb_maxbuf;
00124   Gsasl_ctx *ctx;
00125   int res;
00126   int len;
00127 
00128   ctx = gsasl_client_ctx_get (sctx);
00129   if (ctx == NULL)
00130     return GSASL_CANNOT_GET_CTX;
00131 
00132   /* These are optional */
00133   cb_realm = gsasl_client_callback_realm_get (ctx);
00134   cb_service = gsasl_client_callback_service_get (ctx);
00135   cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
00136   cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx);
00137   cb_qop = gsasl_client_callback_qop_get (ctx);
00138   cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx);
00139 
00140   /* Only optionally needed in infrastructure mode */
00141   cb_password = gsasl_client_callback_password_get (ctx);
00142   if (cb_password == NULL)
00143     return GSASL_NEED_CLIENT_PASSWORD_CALLBACK;
00144 
00145   /* I think we really need this one */
00146   cb_service = gsasl_client_callback_service_get (ctx);
00147   if (cb_service == NULL)
00148     return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
00149 
00150   switch (state->step)
00151     {
00152     case STEP_FIRST:
00153       if (input == NULL)
00154         {
00155           *output_len = 0;
00156           return GSASL_NEEDS_MORE;
00157         }
00158 
00159       if (input_len != SERVER_HELLO_LEN)
00160         return GSASL_MECHANISM_PARSE_ERROR;
00161 
00162       memcpy (state->serverhello, input, input_len);
00163 
00164       {
00165         unsigned char serverbitmap;
00166 
00167         memcpy (&serverbitmap, input, BITMAP_LEN);
00168         state->serverqops = 0;
00169         if (serverbitmap & GSASL_QOP_AUTH)
00170           state->serverqops |= GSASL_QOP_AUTH;
00171         if (serverbitmap & GSASL_QOP_AUTH_INT)
00172           state->serverqops |= GSASL_QOP_AUTH_INT;
00173         if (serverbitmap & GSASL_QOP_AUTH_CONF)
00174           state->serverqops |= GSASL_QOP_AUTH_CONF;
00175         if (serverbitmap & MUTUAL)
00176           state->servermutual = 1;
00177       }
00178       memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
00179       state->servermaxbuf = ntohl (state->servermaxbuf);
00180 
00181       if (cb_qop)
00182         state->clientqop = cb_qop (sctx, state->serverqops);
00183 
00184       if (!(state->serverqops & state->clientqop &
00185             (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF)))
00186         return GSASL_AUTHENTICATION_ERROR;
00187 
00188       /* XXX for now we require server authentication */
00189       if (!state->servermutual)
00190         return GSASL_AUTHENTICATION_ERROR;
00191 
00192       /* Decide policy here: non-infrastructure, infrastructure or proxy.
00193        *
00194        * A callback to decide should be added, but without the default
00195        * should be:
00196        *
00197        * IF shishi_tktset_get_for_server() THEN
00198        *    INFRASTRUCTURE MODE
00199        * ELSE IF shishi_realm_for_server(server) THEN
00200        *    PROXY INFRASTRUCTURE (then fallback to NIM?)
00201        * ELSE
00202        *    NON-INFRASTRUCTURE MODE
00203        */
00204       state->step = STEP_NONINFRA_SEND_APREQ;   /* only NIM for now.. */
00205       /* fall through */
00206 
00207     case STEP_NONINFRA_SEND_ASREQ:
00208       res = shishi_as (state->sh, &state->as);
00209       if (res)
00210         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00211 
00212       if (cb_authentication_id) /* Shishi defaults to one otherwise */
00213         {
00214           len = *output_len - 1;
00215           res = cb_authentication_id (sctx, output, &len);
00216           if (res != GSASL_OK)
00217             return res;
00218           output[len] = '\0';
00219 
00220           res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as),
00221                                          SHISHI_NT_UNKNOWN, output);
00222           if (res != GSASL_OK)
00223             return res;
00224         }
00225 
00226       if (cb_realm)
00227         {
00228           len = *output_len - 1;
00229           res = cb_realm (sctx, output, &len);
00230           if (res != GSASL_OK)
00231             return res;
00232         }
00233       else
00234         len = 0;
00235 
00236       output[len] = '\0';
00237       res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as),
00238                                      output);
00239       if (res != GSASL_OK)
00240         return res;
00241 
00242       if (cb_service)
00243         {
00244           char *sname[3];
00245           size_t servicelen = 0;
00246           size_t hostnamelen = 0;
00247 
00248           res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen,
00249                             /* XXX support servicename a'la DIGEST-MD5 too? */
00250                             NULL, NULL);
00251           if (res != GSASL_OK)
00252             return res;
00253 
00254           if (*output_len < servicelen + 1 + hostnamelen + 1)
00255             return GSASL_TOO_SMALL_BUFFER;
00256 
00257           sname[0] = &output[0];
00258           sname[1] = &output[servicelen + 2];
00259           sname[2] = NULL;
00260 
00261           res = cb_service (sctx, sname[0], &servicelen,
00262                             sname[1], &hostnamelen, NULL, NULL);
00263           if (res != GSASL_OK)
00264             return res;
00265 
00266           sname[0][servicelen] = '\0';
00267           sname[1][hostnamelen] = '\0';
00268 
00269           res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as),
00270                                          SHISHI_NT_UNKNOWN, sname);
00271           if (res != GSASL_OK)
00272             return res;
00273         }
00274 
00275       /* XXX query application for encryption types and set the etype
00276          field?  Already configured by shishi though... */
00277 
00278       res = shishi_a2d (state->sh, shishi_as_req (state->as),
00279                         output, output_len);
00280       if (res != SHISHI_OK)
00281         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00282 
00283       state->step = STEP_NONINFRA_WAIT_ASREP;
00284 
00285       res = GSASL_NEEDS_MORE;
00286       break;
00287 
00288     case STEP_NONINFRA_WAIT_ASREP:
00289       if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK)
00290         return GSASL_MECHANISM_PARSE_ERROR;
00291 
00292       /* XXX? password stored in callee's output buffer */
00293       len = *output_len - 1;
00294       res = cb_password (sctx, output, &len);
00295       if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
00296         return res;
00297       output[len] = '\0';
00298 
00299       res = shishi_as_rep_process (state->as, NULL, output);
00300       if (res != SHISHI_OK)
00301         return GSASL_AUTHENTICATION_ERROR;
00302 
00303       state->step = STEP_NONINFRA_SEND_APREQ;
00304       /* fall through */
00305 
00306     case STEP_NONINFRA_SEND_APREQ:
00307       if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
00308         return GSASL_TOO_SMALL_BUFFER;
00309 
00310       if (!(state->clientqop & ~GSASL_QOP_AUTH))
00311         state->clientmaxbuf = 0;
00312       else if (cb_maxbuf)
00313         state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf);
00314       else
00315         state->clientmaxbuf = MAXBUF_DEFAULT;
00316 
00317       /* XXX for now we require server authentication */
00318       output[0] = state->clientqop | MUTUAL;
00319       {
00320         uint32_t tmp;
00321 
00322         tmp = ntohl (state->clientmaxbuf);
00323         memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN);
00324       }
00325       memcpy (&output[CLIENT_HELLO_LEN], state->serverhello,
00326               SERVER_HELLO_LEN);
00327 
00328       if (cb_authorization_id)
00329         {
00330           len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
00331           res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN +
00332                                                    SERVER_HELLO_LEN], &len);
00333         }
00334       else
00335         len = 0;
00336 
00337       len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
00338       res = shishi_ap_tktoptionsdata (state->sh,
00339                                       &state->ap,
00340                                       shishi_as_tkt (state->as),
00341                                       SHISHI_APOPTIONS_MUTUAL_REQUIRED,
00342                                       output, len);
00343       if (res != SHISHI_OK)
00344         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00345 
00346       res = shishi_authenticator_add_authorizationdata
00347         (state->sh, shishi_ap_authenticator (state->ap), -1, output, len);
00348       if (res != SHISHI_OK)
00349         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00350 
00351       /* XXX set realm in AP-REQ and Authenticator */
00352 
00353       res = shishi_ap_req_der (state->ap, output, output_len);
00354       if (res != SHISHI_OK)
00355         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00356 
00357       state->step = STEP_NONINFRA_WAIT_APREP;
00358 
00359       res = GSASL_NEEDS_MORE;
00360       break;
00361 
00362     case STEP_NONINFRA_WAIT_APREP:
00363       if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK)
00364         return GSASL_MECHANISM_PARSE_ERROR;
00365 
00366       res = shishi_ap_rep_verify (state->ap);
00367       if (res != SHISHI_OK)
00368         return GSASL_AUTHENTICATION_ERROR;
00369 
00370       state->step = STEP_SUCCESS;
00371 
00372       /* XXX support AP session keys */
00373       state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as));
00374 
00375       *output_len = 0;
00376       res = GSASL_OK;
00377       break;
00378 
00379     default:
00380       res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00381       break;
00382     }
00383 
00384   return res;
00385 }
00386 
00387 int
00388 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx,
00389                                   void *mech_data,
00390                                   const char *input,
00391                                   size_t input_len,
00392                                   char *output, size_t * output_len)
00393 {
00394   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00395   int res;
00396 
00397   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00398     {
00399       /* XXX */
00400     }
00401   else if (state && state->sessionkey
00402            && state->clientqop & GSASL_QOP_AUTH_INT)
00403     {
00404       res = shishi_safe (state->sh, &state->safe);
00405       if (res != SHISHI_OK)
00406         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00407 
00408       res = shishi_safe_set_user_data (state->sh,
00409                                        shishi_safe_safe (state->safe),
00410                                        input, input_len);
00411       if (res != SHISHI_OK)
00412         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00413 
00414       res = shishi_safe_build (state->safe, state->sessionkey);
00415       if (res != SHISHI_OK)
00416         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00417 
00418       res = shishi_safe_safe_der (state->safe, output, output_len);
00419       if (res != SHISHI_OK)
00420         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00421     }
00422   else
00423     {
00424       *output_len = input_len;
00425       if (output)
00426         memcpy (output, input, input_len);
00427       return GSASL_OK;
00428     }
00429 
00430   return GSASL_OK;
00431 }
00432 
00433 int
00434 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx,
00435                                   void *mech_data,
00436                                   const char *input,
00437                                   size_t input_len,
00438                                   char *output, size_t * output_len)
00439 {
00440   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00441 
00442   puts ("cdecode");
00443 
00444   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00445     {
00446       /* XXX */
00447     }
00448   else if (state && state->sessionkey
00449            && state->clientqop & GSASL_QOP_AUTH_INT)
00450     {
00451       puts ("decode");
00452     }
00453   else
00454     {
00455       *output_len = input_len;
00456       if (output)
00457         memcpy (output, input, input_len);
00458       return GSASL_OK;
00459     }
00460 
00461   return GSASL_OK;
00462 }
00463 
00464 int
00465 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data)
00466 {
00467   struct _Gsasl_kerberos_v5_client_state *state = mech_data;
00468 
00469   shishi_done (state->sh);
00470   free (state);
00471 
00472   return GSASL_OK;
00473 }
00474 
00475 #endif /* USE_CLIENT */
00476 
00477 /* Server */
00478 
00479 #ifdef USE_SERVER
00480 
00481 struct _Gsasl_kerberos_v5_server_state
00482 {
00483   int firststep;
00484   Shishi *sh;
00485   char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
00486   char *random;
00487   int serverqops;
00488   uint32_t servermaxbuf;
00489   int clientqop;
00490   int clientmutual;
00491   uint32_t clientmaxbuf;
00492   char *username;
00493   char *userrealm;
00494   char *serverrealm;
00495   char *serverservice;
00496   char *serverhostname;
00497   char *password;
00498   Shishi_key *userkey;          /* user's key derived with string2key */
00499   Shishi_key *sessionkey;       /* shared between client and server */
00500   Shishi_key *sessiontktkey;    /* known only by server */
00501   Shishi_ap *ap;
00502   Shishi_as *as;
00503   Shishi_safe *safe;
00504 };
00505 
00506 int
00507 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx)
00508 {
00509   if (!shishi_check_version (SHISHI_VERSION))
00510     return GSASL_UNKNOWN_MECHANISM;
00511 
00512   return GSASL_OK;
00513 }
00514 
00515 int
00516 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data)
00517 {
00518   struct _Gsasl_kerberos_v5_server_state *state;
00519   int err;
00520 
00521   state = malloc (sizeof (*state));
00522   if (state == NULL)
00523     return GSASL_MALLOC_ERROR;
00524   memset (state, 0, sizeof (*state));
00525 
00526   state->random = (char *) malloc (RANDOM_LEN);
00527   if (state->random == NULL)
00528     return GSASL_MALLOC_ERROR;
00529 
00530   err = shishi_init_server (&state->sh);
00531   if (err)
00532     return GSASL_KERBEROS_V5_INIT_ERROR;
00533 
00534   err = shishi_randomize (state->sh, state->random, RANDOM_LEN);
00535   if (err)
00536     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00537 
00538   /* This can be pretty much anything, the client will never have it. */
00539   err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96,
00540                            &state->sessiontktkey);
00541   if (err)
00542     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00543 
00544   err = shishi_as (state->sh, &state->as);
00545   if (err)
00546     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00547 
00548   state->firststep = 1;
00549   state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT;
00550 
00551   *mech_data = state;
00552 
00553   return GSASL_OK;
00554 }
00555 
00556 int
00557 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx,
00558                                 void *mech_data,
00559                                 const char *input,
00560                                 size_t input_len,
00561                                 char *output, size_t * output_len)
00562 {
00563   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00564   Gsasl_server_callback_realm cb_realm;
00565   Gsasl_server_callback_qop cb_qop;
00566   Gsasl_server_callback_maxbuf cb_maxbuf;
00567   Gsasl_server_callback_cipher cb_cipher;
00568   Gsasl_server_callback_retrieve cb_retrieve;
00569   Gsasl_server_callback_service cb_service;
00570   unsigned char buf[BUFSIZ];
00571   size_t buflen;
00572   Gsasl_ctx *ctx;
00573   ASN1_TYPE asn1;
00574   int err;
00575 
00576   ctx = gsasl_server_ctx_get (sctx);
00577   if (ctx == NULL)
00578     return GSASL_CANNOT_GET_CTX;
00579 
00580   cb_realm = gsasl_server_callback_realm_get (ctx);
00581   cb_qop = gsasl_server_callback_qop_get (ctx);
00582   cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx);
00583   cb_retrieve = gsasl_server_callback_retrieve_get (ctx);
00584   cb_service = gsasl_server_callback_service_get (ctx);
00585   if (cb_service == NULL)
00586     return GSASL_NEED_SERVER_SERVICE_CALLBACK;
00587 
00588   if (state->firststep)
00589     {
00590       uint32_t tmp;
00591       unsigned char *p;
00592 
00593       /*
00594        * The initial server packet should contain one octet containing
00595        * a bit mask of supported security layers, four octets
00596        * indicating the maximum cipher-text buffer size the server is
00597        * able to receive (or 0 if no security layers are supported) in
00598        * network byte order, and then 16 octets containing random data
00599        * (see [4] on how random data might be generated).
00600        *
00601        * The security layers and their corresponding bit-masks are as
00602        * follows:
00603        *
00604        *       Bit 0 No security layer
00605        *       Bit 1 Integrity (KRB-SAFE) protection
00606        *       Bit 2 Privacy (KRB-PRIV) protection
00607        *       Bit 3 Mutual authentication is required (AP option MUTUAL-
00608        *             REQUIRED must also be present).
00609        *
00610        * Other bit-masks may be defined in the future; bits which are
00611        * not understood must be negotiated off.
00612        *
00613        */
00614       if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN)
00615         return GSASL_TOO_SMALL_BUFFER;
00616 
00617       p = &state->serverhello[0];
00618 
00619       if (cb_qop)
00620         state->serverqops = cb_qop (sctx);
00621       *p = 0;
00622       if (state->serverqops & GSASL_QOP_AUTH)
00623         *p |= GSASL_QOP_AUTH;
00624       if (state->serverqops & GSASL_QOP_AUTH_INT)
00625         *p |= GSASL_QOP_AUTH_INT;
00626       if (state->serverqops & GSASL_QOP_AUTH_CONF)
00627         *p |= GSASL_QOP_AUTH_CONF;
00628       /* XXX we always require mutual authentication for now */
00629       *p |= MUTUAL;
00630 
00631       if (!(state->serverqops & ~GSASL_QOP_AUTH))
00632         state->servermaxbuf = 0;
00633       else if (cb_maxbuf)
00634         state->servermaxbuf = cb_maxbuf (sctx);
00635       else
00636         state->servermaxbuf = MAXBUF_DEFAULT;
00637 
00638       tmp = htonl (state->servermaxbuf);
00639       memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN);
00640       memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN],
00641               state->random, RANDOM_LEN);
00642 
00643       if (output)
00644         memcpy (output, state->serverhello, SERVER_HELLO_LEN);
00645       *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN;
00646 
00647       state->firststep = 0;
00648 
00649       return GSASL_NEEDS_MORE;
00650     }
00651 
00652   if (cb_retrieve)
00653     {
00654       /* Non-infrastructure mode */
00655 
00656       if (*output_len < 2048)
00657         return GSASL_TOO_SMALL_BUFFER;
00658 
00659       if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK)
00660         {
00661           Shishi_tkt *tkt;
00662           int etype, i;
00663 
00664           tkt = shishi_as_tkt (state->as);
00665           if (!tkt)
00666             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00667 
00668           i = 1;
00669           do
00670             {
00671               err = shishi_kdcreq_etype (state->sh,
00672                                          shishi_as_req (state->as),
00673                                          &etype, i);
00674               if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
00675                 break;
00676             }
00677           while (err == SHISHI_OK);
00678           if (err != SHISHI_OK)
00679             return err;
00680 
00681           /* XXX use a "preferred server kdc etype" from shishi instead? */
00682           err = shishi_key_random (state->sh, etype, &state->sessionkey);
00683           if (err)
00684             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00685 
00686           err = shishi_tkt_key_set (tkt, state->sessionkey);
00687           if (err)
00688             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00689 
00690           buflen = sizeof (buf) - 1;
00691           err = shishi_kdcreq_cname_get (state->sh,
00692                                          shishi_as_req (state->as),
00693                                          buf, &buflen);
00694           if (err != SHISHI_OK)
00695             return err;
00696           buf[buflen] = '\0';
00697           state->username = strdup (buf);
00698 
00699           buflen = sizeof (buf) - 1;
00700           err = shishi_kdcreq_realm_get (state->sh,
00701                                          shishi_as_req (state->as),
00702                                          buf, &buflen);
00703           if (err != SHISHI_OK)
00704             return err;
00705           buf[buflen] = '\0';
00706           state->userrealm = strdup (buf);
00707 
00708           buflen = sizeof (buf) - 1;
00709           err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
00710                              NULL, &buflen);
00711           if (err != GSASL_OK)
00712             return err;
00713 
00714           state->password = malloc (buflen + 1);
00715           if (state->password == NULL)
00716             return GSASL_MALLOC_ERROR;
00717 
00718           err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
00719                              state->password, &buflen);
00720           if (err != GSASL_OK)
00721             return err;
00722           state->password[buflen] = '\0';
00723 
00724           buflen = sizeof (buf) - 1;
00725           if (cb_realm)
00726             {
00727               err = cb_realm (sctx, buf, &buflen, 0);
00728               if (err != GSASL_OK)
00729                 return err;
00730             }
00731           else
00732             buflen = 0;
00733           buf[buflen] = '\0';
00734           state->serverrealm = strdup (buf);
00735 
00736           buflen = sizeof (buf) - 1;
00737           err = cb_service (sctx, buf, &buflen, NULL, NULL);
00738           if (err != GSASL_OK)
00739             return err;
00740           buf[buflen] = '\0';
00741           state->serverservice = strdup (buf);
00742 
00743           buflen = sizeof (buf) - 1;
00744           err = cb_service (sctx, NULL, NULL, buf, &buflen);
00745           if (err != GSASL_OK)
00746             return err;
00747           buf[buflen] = '\0';
00748           state->serverhostname = strdup (buf);
00749 
00750           /* XXX do some checking on realm and server name?  Right now
00751              we simply doesn't care about what client requested and
00752              return a ticket for this server.  This is bad. */
00753 
00754           err = shishi_tkt_clientrealm_set (tkt, state->userrealm,
00755                                             state->username);
00756           if (err)
00757             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00758 
00759           {
00760             char *p;
00761             p = malloc (strlen (state->serverservice) + strlen ("/") +
00762                         strlen (state->serverhostname) + 1);
00763             if (p == NULL)
00764               return GSASL_MALLOC_ERROR;
00765             sprintf (p, "%s/%s", state->serverservice, state->serverhostname);
00766             err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p);
00767             free (p);
00768             if (err)
00769               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00770           }
00771 
00772           buflen = sizeof (buf);
00773           err = shishi_as_derive_salt (state->sh,
00774                                        shishi_as_req (state->as),
00775                                        shishi_as_rep (state->as),
00776                                        buf, &buflen);
00777           if (err != SHISHI_OK)
00778             return err;
00779 
00780           err = shishi_key_from_string (state->sh,
00781                                         etype,
00782                                         state->password,
00783                                         strlen (state->password),
00784                                         buf, buflen, NULL, &state->userkey);
00785           if (err != SHISHI_OK)
00786             return err;
00787 
00788           err = shishi_tkt_build (tkt, state->sessiontktkey);
00789           if (err)
00790             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00791 
00792           err = shishi_as_rep_build (state->as, state->userkey);
00793           if (err)
00794             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00795 
00796 #if DEBUG
00797           shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as));
00798           shishi_encticketpart_print (state->sh, stderr,
00799                                       shishi_tkt_encticketpart (tkt));
00800           shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt));
00801           shishi_enckdcreppart_print (state->sh, stderr,
00802                                       shishi_tkt_enckdcreppart (state->as));
00803           shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as));
00804 #endif
00805 
00806           err = shishi_as_rep_der (state->as, output, output_len);
00807           if (err)
00808             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00809 
00810           return GSASL_NEEDS_MORE;
00811         }
00812       else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len)))
00813         {
00814           int adtype;
00815 
00816           err = shishi_ap (state->sh, &state->ap);
00817           if (err)
00818             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00819 
00820           shishi_ap_req_set (state->ap, asn1);
00821 
00822           err = shishi_ap_req_process (state->ap, state->sessiontktkey);
00823           if (err)
00824             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00825 
00826 #if DEBUG
00827           shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap));
00828           shishi_ticket_print (state->sh, stderr,
00829                                shishi_tkt_ticket (shishi_ap_tkt (state->ap)));
00830           shishi_authenticator_print (state->sh, stderr,
00831                                       shishi_ap_authenticator (state->ap));
00832 #endif
00833 
00834           buflen = sizeof (buf);
00835           err = shishi_authenticator_authorizationdata
00836             (state->sh, shishi_ap_authenticator (state->ap),
00837              &adtype, buf, &buflen, 1);
00838           if (err)
00839             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00840 
00841           if (adtype != 0xFF /* -1 in one-complements form */  ||
00842               buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
00843             return GSASL_AUTHENTICATION_ERROR;
00844 
00845           {
00846             unsigned char clientbitmap;
00847 
00848             memcpy (&clientbitmap, &buf[0], BITMAP_LEN);
00849             state->clientqop = 0;
00850             if (clientbitmap & GSASL_QOP_AUTH)
00851               state->clientqop |= GSASL_QOP_AUTH;
00852             if (clientbitmap & GSASL_QOP_AUTH_INT)
00853               state->clientqop |= GSASL_QOP_AUTH_INT;
00854             if (clientbitmap & GSASL_QOP_AUTH_CONF)
00855               state->clientqop |= GSASL_QOP_AUTH_CONF;
00856             if (clientbitmap & MUTUAL)
00857               state->clientmutual = 1;
00858           }
00859           memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
00860           state->clientmaxbuf = ntohl (state->clientmaxbuf);
00861 
00862           if (!(state->clientqop & state->serverqops))
00863             return GSASL_AUTHENTICATION_ERROR;
00864 
00865           /* XXX check clientmaxbuf too */
00866 
00867           if (memcmp (&buf[CLIENT_HELLO_LEN],
00868                       state->serverhello, SERVER_HELLO_LEN) != 0)
00869             return GSASL_AUTHENTICATION_ERROR;
00870 
00871           {
00872             char cksum[BUFSIZ];
00873             int cksumlen;
00874             int cksumtype;
00875             Shishi_key *key;
00876 
00877             key = shishi_tkt_key (shishi_as_tkt (state->as));
00878             cksumtype =
00879               shishi_cipher_defaultcksumtype (shishi_key_type (key));
00880             cksumlen = sizeof (cksum);
00881             err = shishi_checksum (state->sh, key,
00882                                    SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM,
00883                                    cksumtype, buf, buflen, cksum, &cksumlen);
00884             if (err != SHISHI_OK)
00885               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00886 
00887             buflen = sizeof (buf);
00888             err = shishi_authenticator_cksum
00889               (state->sh,
00890                shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen);
00891             if (err != SHISHI_OK)
00892               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00893 
00894             if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0)
00895               return GSASL_AUTHENTICATION_ERROR;
00896           }
00897 
00898           /* XXX use authorization_id */
00899 
00900           if (state->clientmutual)
00901             {
00902               err = shishi_ap_rep_build (state->ap);
00903               if (err)
00904                 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00905 
00906               err = shishi_ap_rep_der (state->ap, output, output_len);
00907               if (err)
00908                 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00909             }
00910           else
00911             *output_len = 0;
00912 
00913           return GSASL_OK;
00914         }
00915     }
00916   else
00917     {
00918       /* XXX Currently we only handle AS-REQ and AP-REQ in
00919          non-infrastructure mode.  Supporting infrastructure mode is
00920          simple, just send the AS-REQ to the KDC and wait for AS-REP
00921          instead of creating AS-REP locally.
00922 
00923          We should probably have a callback to decide policy:
00924          1) non-infrastructure mode (NIM) only
00925          2) infrastructure mode (IM) only
00926          3) proxied infrastructure mode (PIM) only
00927          4) NIM with fallback to IM (useful for local server overrides)
00928          5) IM with fallback to NIM (useful for admins if KDC is offline)
00929          6) ...etc with PIM too
00930        */
00931       return GSASL_NEED_SERVER_RETRIEVE_CALLBACK;
00932     }
00933 
00934   *output_len = 0;
00935   return GSASL_NEEDS_MORE;
00936 }
00937 
00938 int
00939 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx,
00940                                   void *mech_data,
00941                                   const char *input,
00942                                   size_t input_len,
00943                                   char *output, size_t * output_len)
00944 {
00945   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00946   int res;
00947 
00948   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00949     {
00950       /* XXX */
00951     }
00952   else if (state && state->sessionkey
00953            && state->clientqop & GSASL_QOP_AUTH_INT)
00954     {
00955       res = shishi_safe (state->sh, &state->safe);
00956       if (res != SHISHI_OK)
00957         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00958 
00959       res = shishi_safe_set_user_data (state->sh,
00960                                        shishi_safe_safe (state->safe),
00961                                        input, input_len);
00962       if (res != SHISHI_OK)
00963         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00964 
00965       res = shishi_safe_build (state->safe, state->sessionkey);
00966       if (res != SHISHI_OK)
00967         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00968 
00969       res = shishi_safe_safe_der (state->safe, output, output_len);
00970       if (res != SHISHI_OK)
00971         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00972     }
00973   else
00974     {
00975       *output_len = input_len;
00976       if (output)
00977         memcpy (output, input, input_len);
00978       return GSASL_OK;
00979     }
00980 
00981   return GSASL_OK;
00982 }
00983 
00984 int
00985 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx,
00986                                   void *mech_data,
00987                                   const char *input,
00988                                   size_t input_len,
00989                                   char *output, size_t * output_len)
00990 {
00991   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00992   int res;
00993 
00994   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00995     {
00996       /* XXX */
00997     }
00998   else if (state && state->sessionkey
00999            && state->clientqop & GSASL_QOP_AUTH_INT)
01000     {
01001       Shishi_asn1 asn1safe;
01002 
01003       res = shishi_safe (state->sh, &state->safe);
01004       if (res != SHISHI_OK)
01005         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
01006 
01007       res = shishi_safe_safe_der_set (state->safe, input, input_len);
01008       printf ("len %d err %d\n", input_len, res);
01009       if (res != SHISHI_OK)
01010         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
01011 
01012       res = shishi_safe_verify (state->safe, state->sessionkey);
01013       if (res != SHISHI_OK)
01014         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
01015 
01016       res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe),
01017                                    output, output_len);
01018       if (res != SHISHI_OK)
01019         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
01020       printf ("len=%d\n", *output_len);
01021       return GSASL_OK;
01022     }
01023   else
01024     {
01025       *output_len = input_len;
01026       if (output)
01027         memcpy (output, input, input_len);
01028       return GSASL_OK;
01029     }
01030 
01031 
01032   return GSASL_OK;
01033 }
01034 
01035 int
01036 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data)
01037 {
01038   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
01039 
01040   shishi_done (state->sh);
01041 
01042   free (state->username);
01043   free (state->password);
01044   free (state->random);
01045   free (state);
01046 
01047   return GSASL_OK;
01048 }
01049 #endif /* USE_SERVER */