/* * Copyright (c) 2011, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of JANET(UK) nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * This code was adapted from the MIT Kerberos Consortium's * GSS example code, which was distributed under the following * license: * * Copyright 2004-2006 Massachusetts Institute of Technology. * All Rights Reserved. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include "gsscon.h" const char *gServiceName = NULL; int gsscon_passive_authenticate (int inSocket, gss_ctx_id_t *outGSSContext) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0; gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; char *inputTokenBuffer = NULL; size_t inputTokenBufferLength = 0; gss_buffer_desc inputToken; /* buffer received from the server */ if (inSocket < 0 ) { err = EINVAL; } if (!outGSSContext) { err = EINVAL; } /* * The main authentication loop: * * GSS is a multimechanism API. The number of packet exchanges required to * authenticatevaries between mechanisms. As a result, we need to loop reading * input tokens from the client, calling gss_accept_sec_context on the input * tokens and send the resulting output tokens back to the client until we * get GSS_S_COMPLETE or an error. * * When we are done, save the client principal so we can make authorization * checks. */ majorStatus = GSS_S_CONTINUE_NEEDED; while (!err && (majorStatus != GSS_S_COMPLETE)) { /* Clean up old input buffer */ if (inputTokenBuffer != NULL) { free (inputTokenBuffer); inputTokenBuffer = NULL; /* don't double-free */ } err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength); if (!err) { /* Set up input buffers for the next run through the loop */ inputToken.value = inputTokenBuffer; inputToken.length = inputTokenBufferLength; } if (!err) { /* buffer to send to the server */ gss_buffer_desc outputToken = { 0, NULL }; /* * accept_sec_context does the actual work of taking the client's * request and generating an appropriate reply. Note that we pass * GSS_C_NO_CREDENTIAL for the service principal. This causes the * server to accept any service principal in the server's keytab, * which enables you to support multihomed hosts by having one key * in the keytab for each host identity the server responds on. * * However, since we may have more keys in the keytab than we want * the server to actually use, we will need to check which service * principal the client used after authentication succeeds. See * ServicePrincipalIsValidForService() for where you would put these * checks. We don't check here since if we stopped responding in the * middle of the authentication negotiation, the client would get an * EOF, and the user wouldn't know what went wrong. */ printf ("Calling gss_accept_sec_context...\n"); majorStatus = gss_accept_sec_context (&minorStatus, &gssContext, GSS_C_NO_CREDENTIAL, &inputToken, GSS_C_NO_CHANNEL_BINDINGS, NULL /* client_name */, NULL /* actual_mech_type */, &outputToken, NULL /* req_flags */, NULL /* time_rec */, NULL /* delegated_cred_handle */); if ((outputToken.length > 0) && (outputToken.value != NULL)) { /* Send the output token to the client (even on error) */ err = WriteToken (inSocket, outputToken.value, outputToken.length); /* free the output token */ gss_release_buffer (&minorStatus, &outputToken); } } if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { printGSSErrors ("gss_accept_sec_context", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } if (!err) { *outGSSContext = gssContext; gssContext = NULL; } else { printError (err, "Authenticate failed"); } if (inputTokenBuffer) { free (inputTokenBuffer); } if (gssContext != GSS_C_NO_CONTEXT) { gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } return err; } /* --------------------------------------------------------------------------- */ static int ServicePrincipalIsValidForService (const char *inServicePrincipal) { int err = 0; krb5_context context = NULL; krb5_principal principal = NULL; if (!inServicePrincipal) { err = EINVAL; } if (!err) { err = krb5_init_context (&context); } if (!err) { err = krb5_parse_name (context, inServicePrincipal, &principal); } if (!err) { /* * Here is where we check to see if the service principal the client * used is valid. Typically we would just check that the first component * is the name of the service provided by the server. This check exists * to make sure the server is using the correct key in its keytab since * we passed GSS_C_NO_CREDENTIAL into gss_accept_sec_context(). */ if (gServiceName && strcmp (gServiceName, krb5_princ_name (context, principal)->data) != 0) { err = KRB5KRB_AP_WRONG_PRINC; } } if (principal) { krb5_free_principal (context, principal); } if (context ) { krb5_free_context (context); } return err; } /* --------------------------------------------------------------------------- */ static int ClientPrincipalIsAuthorizedForService (const char *inClientPrincipal) { int err = 0; krb5_context context = NULL; krb5_principal principal = NULL; if (!inClientPrincipal) { err = EINVAL; } if (!err) { err = krb5_init_context (&context); } if (!err) { err = krb5_parse_name (context, inClientPrincipal, &principal); } if (!err) { /* * Here is where the server checks to see if the client principal should * be allowed to use your service. Typically it should check both the name * and the realm, since with cross-realm shared keys, a user at another * realm may be trying to contact your service. */ err = 0; } if (principal) { krb5_free_principal (context, principal); } if (context ) { krb5_free_context (context); } return err; } /* --------------------------------------------------------------------------- */ int gsscon_authorize (gss_ctx_id_t inContext, int *outAuthorized, int *outAuthorizationError) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0; gss_name_t clientName = NULL; gss_name_t serviceName = NULL; char *clientPrincipal = NULL; char *servicePrincipal = NULL; if (!inContext ) { err = EINVAL; } if (!outAuthorized ) { err = EINVAL; } if (!outAuthorizationError) { err = EINVAL; } if (!err) { /* Get the client and service principals used to authenticate */ majorStatus = gss_inquire_context (&minorStatus, inContext, &clientName, &serviceName, NULL, NULL, NULL, NULL, NULL); if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } } if (!err) { /* Pull the client principal string out of the gss name */ gss_buffer_desc nameToken; majorStatus = gss_display_name (&minorStatus, clientName, &nameToken, NULL); if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } if (!err) { clientPrincipal = malloc (nameToken.length + 1); if (clientPrincipal == NULL) { err = ENOMEM; } } if (!err) { memcpy (clientPrincipal, nameToken.value, nameToken.length); clientPrincipal[nameToken.length] = '\0'; } if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } } if (!err) { /* Pull the service principal string out of the gss name */ gss_buffer_desc nameToken; majorStatus = gss_display_name (&minorStatus, serviceName, &nameToken, NULL); if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } if (!err) { servicePrincipal = malloc (nameToken.length + 1); if (servicePrincipal == NULL) { err = ENOMEM; } } if (!err) { memcpy (servicePrincipal, nameToken.value, nameToken.length); servicePrincipal[nameToken.length] = '\0'; } if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } } if (!err) { int authorizationErr = ServicePrincipalIsValidForService (servicePrincipal); if (!authorizationErr) { authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal); } printf ("'%s' is%s authorized for service '%s'\n", clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal); *outAuthorized = !authorizationErr; *outAuthorizationError = authorizationErr; } if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } if (clientName ) { gss_release_name (&minorStatus, &clientName); } if (clientPrincipal ) { free (clientPrincipal); } if (servicePrincipal) { free (servicePrincipal); } return err; }