gsasl  1.8.0
kerberos_v5/server.c
Go to the documentation of this file.
00001 /* server.c --- Experimental SASL mechanism KERBEROS_V5, server 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_server_state
00031 {
00032   int firststep;
00033   Shishi *sh;
00034   char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
00035   char *random;
00036   int serverqops;
00037   uint32_t servermaxbuf;
00038   int clientqop;
00039   int clientmutual;
00040   uint32_t clientmaxbuf;
00041   char *username;
00042   char *userrealm;
00043   char *serverrealm;
00044   char *serverservice;
00045   char *serverhostname;
00046   char *password;
00047   Shishi_key *userkey;          /* user's key derived with string2key */
00048   Shishi_key *sessionkey;       /* shared between client and server */
00049   Shishi_key *sessiontktkey;    /* known only by server */
00050   Shishi_ap *ap;
00051   Shishi_as *as;
00052   Shishi_safe *safe;
00053 };
00054 
00055 int
00056 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx)
00057 {
00058   if (!shishi_check_version (SHISHI_VERSION))
00059     return GSASL_UNKNOWN_MECHANISM;
00060 
00061   return GSASL_OK;
00062 }
00063 
00064 int
00065 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data)
00066 {
00067   struct _Gsasl_kerberos_v5_server_state *state;
00068   int err;
00069 
00070   state = malloc (sizeof (*state));
00071   if (state == NULL)
00072     return GSASL_MALLOC_ERROR;
00073   memset (state, 0, sizeof (*state));
00074 
00075   state->random = (char *) malloc (RANDOM_LEN);
00076   if (state->random == NULL)
00077     return GSASL_MALLOC_ERROR;
00078 
00079   err = shishi_init_server (&state->sh);
00080   if (err)
00081     return GSASL_KERBEROS_V5_INIT_ERROR;
00082 
00083   err = shishi_randomize (state->sh, state->random, RANDOM_LEN);
00084   if (err)
00085     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00086 
00087   /* This can be pretty much anything, the client will never have it. */
00088   err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96,
00089                            &state->sessiontktkey);
00090   if (err)
00091     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00092 
00093   err = shishi_as (state->sh, &state->as);
00094   if (err)
00095     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00096 
00097   state->firststep = 1;
00098   state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT;
00099 
00100   *mech_data = state;
00101 
00102   return GSASL_OK;
00103 }
00104 
00105 int
00106 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx,
00107                                 void *mech_data,
00108                                 const char *input,
00109                                 size_t input_len,
00110                                 char *output, size_t * output_len)
00111 {
00112   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00113   Gsasl_server_callback_realm cb_realm;
00114   Gsasl_server_callback_qop cb_qop;
00115   Gsasl_server_callback_maxbuf cb_maxbuf;
00116   Gsasl_server_callback_cipher cb_cipher;
00117   Gsasl_server_callback_retrieve cb_retrieve;
00118   Gsasl_server_callback_service cb_service;
00119   unsigned char buf[BUFSIZ];
00120   size_t buflen;
00121   Gsasl_ctx *ctx;
00122   ASN1_TYPE asn1;
00123   int err;
00124 
00125   ctx = gsasl_server_ctx_get (sctx);
00126   if (ctx == NULL)
00127     return GSASL_CANNOT_GET_CTX;
00128 
00129   cb_realm = gsasl_server_callback_realm_get (ctx);
00130   cb_qop = gsasl_server_callback_qop_get (ctx);
00131   cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx);
00132   cb_retrieve = gsasl_server_callback_retrieve_get (ctx);
00133   cb_service = gsasl_server_callback_service_get (ctx);
00134   if (cb_service == NULL)
00135     return GSASL_NEED_SERVER_SERVICE_CALLBACK;
00136 
00137   if (state->firststep)
00138     {
00139       uint32_t tmp;
00140       unsigned char *p;
00141 
00142       /*
00143        * The initial server packet should contain one octet containing
00144        * a bit mask of supported security layers, four octets
00145        * indicating the maximum cipher-text buffer size the server is
00146        * able to receive (or 0 if no security layers are supported) in
00147        * network byte order, and then 16 octets containing random data
00148        * (see [4] on how random data might be generated).
00149        *
00150        * The security layers and their corresponding bit-masks are as
00151        * follows:
00152        *
00153        *       Bit 0 No security layer
00154        *       Bit 1 Integrity (KRB-SAFE) protection
00155        *       Bit 2 Privacy (KRB-PRIV) protection
00156        *       Bit 3 Mutual authentication is required (AP option MUTUAL-
00157        *             REQUIRED must also be present).
00158        *
00159        * Other bit-masks may be defined in the future; bits which are
00160        * not understood must be negotiated off.
00161        *
00162        */
00163       if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN)
00164         return GSASL_TOO_SMALL_BUFFER;
00165 
00166       p = &state->serverhello[0];
00167 
00168       if (cb_qop)
00169         state->serverqops = cb_qop (sctx);
00170       *p = 0;
00171       if (state->serverqops & GSASL_QOP_AUTH)
00172         *p |= GSASL_QOP_AUTH;
00173       if (state->serverqops & GSASL_QOP_AUTH_INT)
00174         *p |= GSASL_QOP_AUTH_INT;
00175       if (state->serverqops & GSASL_QOP_AUTH_CONF)
00176         *p |= GSASL_QOP_AUTH_CONF;
00177       /* XXX we always require mutual authentication for now */
00178       *p |= MUTUAL;
00179 
00180       if (!(state->serverqops & ~GSASL_QOP_AUTH))
00181         state->servermaxbuf = 0;
00182       else if (cb_maxbuf)
00183         state->servermaxbuf = cb_maxbuf (sctx);
00184       else
00185         state->servermaxbuf = MAXBUF_DEFAULT;
00186 
00187       tmp = htonl (state->servermaxbuf);
00188       memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN);
00189       memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN],
00190               state->random, RANDOM_LEN);
00191 
00192       if (output)
00193         memcpy (output, state->serverhello, SERVER_HELLO_LEN);
00194       *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN;
00195 
00196       state->firststep = 0;
00197 
00198       return GSASL_NEEDS_MORE;
00199     }
00200 
00201   if (cb_retrieve)
00202     {
00203       /* Non-infrastructure mode */
00204 
00205       if (*output_len < 2048)
00206         return GSASL_TOO_SMALL_BUFFER;
00207 
00208       if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK)
00209         {
00210           Shishi_tkt *tkt;
00211           int etype, i;
00212 
00213           tkt = shishi_as_tkt (state->as);
00214           if (!tkt)
00215             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00216 
00217           i = 1;
00218           do
00219             {
00220               err = shishi_kdcreq_etype (state->sh,
00221                                          shishi_as_req (state->as),
00222                                          &etype, i);
00223               if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
00224                 break;
00225             }
00226           while (err == SHISHI_OK);
00227           if (err != SHISHI_OK)
00228             return err;
00229 
00230           /* XXX use a "preferred server kdc etype" from shishi instead? */
00231           err = shishi_key_random (state->sh, etype, &state->sessionkey);
00232           if (err)
00233             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00234 
00235           err = shishi_tkt_key_set (tkt, state->sessionkey);
00236           if (err)
00237             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00238 
00239           buflen = sizeof (buf) - 1;
00240           err = shishi_kdcreq_cname_get (state->sh,
00241                                          shishi_as_req (state->as),
00242                                          buf, &buflen);
00243           if (err != SHISHI_OK)
00244             return err;
00245           buf[buflen] = '\0';
00246           state->username = strdup (buf);
00247 
00248           buflen = sizeof (buf) - 1;
00249           err = shishi_kdcreq_realm_get (state->sh,
00250                                          shishi_as_req (state->as),
00251                                          buf, &buflen);
00252           if (err != SHISHI_OK)
00253             return err;
00254           buf[buflen] = '\0';
00255           state->userrealm = strdup (buf);
00256 
00257           buflen = sizeof (buf) - 1;
00258           err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
00259                              NULL, &buflen);
00260           if (err != GSASL_OK)
00261             return err;
00262 
00263           state->password = malloc (buflen + 1);
00264           if (state->password == NULL)
00265             return GSASL_MALLOC_ERROR;
00266 
00267           err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
00268                              state->password, &buflen);
00269           if (err != GSASL_OK)
00270             return err;
00271           state->password[buflen] = '\0';
00272 
00273           buflen = sizeof (buf) - 1;
00274           if (cb_realm)
00275             {
00276               err = cb_realm (sctx, buf, &buflen, 0);
00277               if (err != GSASL_OK)
00278                 return err;
00279             }
00280           else
00281             buflen = 0;
00282           buf[buflen] = '\0';
00283           state->serverrealm = strdup (buf);
00284 
00285           buflen = sizeof (buf) - 1;
00286           err = cb_service (sctx, buf, &buflen, NULL, NULL);
00287           if (err != GSASL_OK)
00288             return err;
00289           buf[buflen] = '\0';
00290           state->serverservice = strdup (buf);
00291 
00292           buflen = sizeof (buf) - 1;
00293           err = cb_service (sctx, NULL, NULL, buf, &buflen);
00294           if (err != GSASL_OK)
00295             return err;
00296           buf[buflen] = '\0';
00297           state->serverhostname = strdup (buf);
00298 
00299           /* XXX do some checking on realm and server name?  Right now
00300              we simply doesn't care about what client requested and
00301              return a ticket for this server.  This is bad. */
00302 
00303           err = shishi_tkt_clientrealm_set (tkt, state->userrealm,
00304                                             state->username);
00305           if (err)
00306             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00307 
00308           {
00309             char *p;
00310             p = malloc (strlen (state->serverservice) + strlen ("/") +
00311                         strlen (state->serverhostname) + 1);
00312             if (p == NULL)
00313               return GSASL_MALLOC_ERROR;
00314             sprintf (p, "%s/%s", state->serverservice, state->serverhostname);
00315             err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p);
00316             free (p);
00317             if (err)
00318               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00319           }
00320 
00321           buflen = sizeof (buf);
00322           err = shishi_as_derive_salt (state->sh,
00323                                        shishi_as_req (state->as),
00324                                        shishi_as_rep (state->as),
00325                                        buf, &buflen);
00326           if (err != SHISHI_OK)
00327             return err;
00328 
00329           err = shishi_key_from_string (state->sh,
00330                                         etype,
00331                                         state->password,
00332                                         strlen (state->password),
00333                                         buf, buflen, NULL, &state->userkey);
00334           if (err != SHISHI_OK)
00335             return err;
00336 
00337           err = shishi_tkt_build (tkt, state->sessiontktkey);
00338           if (err)
00339             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00340 
00341           err = shishi_as_rep_build (state->as, state->userkey);
00342           if (err)
00343             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00344 
00345 #if DEBUG
00346           shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as));
00347           shishi_encticketpart_print (state->sh, stderr,
00348                                       shishi_tkt_encticketpart (tkt));
00349           shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt));
00350           shishi_enckdcreppart_print (state->sh, stderr,
00351                                       shishi_tkt_enckdcreppart (state->as));
00352           shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as));
00353 #endif
00354 
00355           err = shishi_as_rep_der (state->as, output, output_len);
00356           if (err)
00357             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00358 
00359           return GSASL_NEEDS_MORE;
00360         }
00361       else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len)))
00362         {
00363           int adtype;
00364 
00365           err = shishi_ap (state->sh, &state->ap);
00366           if (err)
00367             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00368 
00369           shishi_ap_req_set (state->ap, asn1);
00370 
00371           err = shishi_ap_req_process (state->ap, state->sessiontktkey);
00372           if (err)
00373             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00374 
00375 #if DEBUG
00376           shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap));
00377           shishi_ticket_print (state->sh, stderr,
00378                                shishi_tkt_ticket (shishi_ap_tkt (state->ap)));
00379           shishi_authenticator_print (state->sh, stderr,
00380                                       shishi_ap_authenticator (state->ap));
00381 #endif
00382 
00383           buflen = sizeof (buf);
00384           err = shishi_authenticator_authorizationdata
00385             (state->sh, shishi_ap_authenticator (state->ap),
00386              &adtype, buf, &buflen, 1);
00387           if (err)
00388             return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00389 
00390           if (adtype != 0xFF /* -1 in one-complements form */  ||
00391               buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
00392             return GSASL_AUTHENTICATION_ERROR;
00393 
00394           {
00395             unsigned char clientbitmap;
00396 
00397             memcpy (&clientbitmap, &buf[0], BITMAP_LEN);
00398             state->clientqop = 0;
00399             if (clientbitmap & GSASL_QOP_AUTH)
00400               state->clientqop |= GSASL_QOP_AUTH;
00401             if (clientbitmap & GSASL_QOP_AUTH_INT)
00402               state->clientqop |= GSASL_QOP_AUTH_INT;
00403             if (clientbitmap & GSASL_QOP_AUTH_CONF)
00404               state->clientqop |= GSASL_QOP_AUTH_CONF;
00405             if (clientbitmap & MUTUAL)
00406               state->clientmutual = 1;
00407           }
00408           memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
00409           state->clientmaxbuf = ntohl (state->clientmaxbuf);
00410 
00411           if (!(state->clientqop & state->serverqops))
00412             return GSASL_AUTHENTICATION_ERROR;
00413 
00414           /* XXX check clientmaxbuf too */
00415 
00416           if (memcmp (&buf[CLIENT_HELLO_LEN],
00417                       state->serverhello, SERVER_HELLO_LEN) != 0)
00418             return GSASL_AUTHENTICATION_ERROR;
00419 
00420           {
00421             char cksum[BUFSIZ];
00422             int cksumlen;
00423             int cksumtype;
00424             Shishi_key *key;
00425 
00426             key = shishi_tkt_key (shishi_as_tkt (state->as));
00427             cksumtype =
00428               shishi_cipher_defaultcksumtype (shishi_key_type (key));
00429             cksumlen = sizeof (cksum);
00430             err = shishi_checksum (state->sh, key,
00431                                    SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM,
00432                                    cksumtype, buf, buflen, cksum, &cksumlen);
00433             if (err != SHISHI_OK)
00434               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00435 
00436             buflen = sizeof (buf);
00437             err = shishi_authenticator_cksum
00438               (state->sh,
00439                shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen);
00440             if (err != SHISHI_OK)
00441               return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00442 
00443             if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0)
00444               return GSASL_AUTHENTICATION_ERROR;
00445           }
00446 
00447           /* XXX use authorization_id */
00448 
00449           if (state->clientmutual)
00450             {
00451               err = shishi_ap_rep_build (state->ap);
00452               if (err)
00453                 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00454 
00455               err = shishi_ap_rep_der (state->ap, output, output_len);
00456               if (err)
00457                 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00458             }
00459           else
00460             *output_len = 0;
00461 
00462           return GSASL_OK;
00463         }
00464     }
00465   else
00466     {
00467       /* XXX Currently we only handle AS-REQ and AP-REQ in
00468          non-infrastructure mode.  Supporting infrastructure mode is
00469          simple, just send the AS-REQ to the KDC and wait for AS-REP
00470          instead of creating AS-REP locally.
00471 
00472          We should probably have a callback to decide policy:
00473          1) non-infrastructure mode (NIM) only
00474          2) infrastructure mode (IM) only
00475          3) proxied infrastructure mode (PIM) only
00476          4) NIM with fallback to IM (useful for local server overrides)
00477          5) IM with fallback to NIM (useful for admins if KDC is offline)
00478          6) ...etc with PIM too
00479        */
00480       return GSASL_NEED_SERVER_RETRIEVE_CALLBACK;
00481     }
00482 
00483   *output_len = 0;
00484   return GSASL_NEEDS_MORE;
00485 }
00486 
00487 int
00488 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx,
00489                                   void *mech_data,
00490                                   const char *input,
00491                                   size_t input_len,
00492                                   char *output, size_t * output_len)
00493 {
00494   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00495   int res;
00496 
00497   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00498     {
00499       return GSASL_INTEGRITY_ERROR;
00500     }
00501   else if (state && state->sessionkey
00502            && state->clientqop & GSASL_QOP_AUTH_INT)
00503     {
00504       res = shishi_safe (state->sh, &state->safe);
00505       if (res != SHISHI_OK)
00506         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00507 
00508       res = shishi_safe_set_user_data (state->sh,
00509                                        shishi_safe_safe (state->safe),
00510                                        input, input_len);
00511       if (res != SHISHI_OK)
00512         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00513 
00514       res = shishi_safe_build (state->safe, state->sessionkey);
00515       if (res != SHISHI_OK)
00516         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00517 
00518       res = shishi_safe_safe_der (state->safe, output, output_len);
00519       if (res != SHISHI_OK)
00520         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00521     }
00522   else
00523     {
00524       *output_len = input_len;
00525       *output = malloc (input_len);
00526       if (!*output)
00527         return GSASL_MALLOC_ERROR;
00528       memcpy (*output, input, input_len);
00529     }
00530 
00531   return GSASL_OK;
00532 }
00533 
00534 int
00535 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx,
00536                                   void *mech_data,
00537                                   const char *input,
00538                                   size_t input_len,
00539                                   char *output, size_t * output_len)
00540 {
00541   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00542   int res;
00543 
00544   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
00545     {
00546       return GSASL_INTEGRITY_ERROR;
00547     }
00548   else if (state && state->sessionkey
00549            && state->clientqop & GSASL_QOP_AUTH_INT)
00550     {
00551       Shishi_asn1 asn1safe;
00552 
00553       res = shishi_safe (state->sh, &state->safe);
00554       if (res != SHISHI_OK)
00555         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00556 
00557       res = shishi_safe_safe_der_set (state->safe, input, input_len);
00558       if (res != SHISHI_OK)
00559         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00560 
00561       res = shishi_safe_verify (state->safe, state->sessionkey);
00562       if (res != SHISHI_OK)
00563         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00564 
00565       res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe),
00566                                    output, output_len);
00567       if (res != SHISHI_OK)
00568         return GSASL_KERBEROS_V5_INTERNAL_ERROR;
00569 
00570       return GSASL_OK;
00571     }
00572   else
00573     {
00574       *output_len = input_len;
00575       *output = malloc (input_len);
00576       if (!*output)
00577         return GSASL_MALLOC_ERROR;
00578       memcpy (*output, input, input_len);
00579     }
00580 
00581 
00582   return GSASL_OK;
00583 }
00584 
00585 int
00586 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data)
00587 {
00588   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
00589 
00590   shishi_done (state->sh);
00591 
00592   free (state->username);
00593   free (state->password);
00594   free (state->random);
00595   free (state);
00596 
00597   return GSASL_OK;
00598 }