mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +01:00 
			
		
		
		
	Make sure to release memory, which was allocated by gss_import_name(). Also when plugin_init() fails.
		
			
				
	
	
		
			276 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <my_config.h>
 | 
						|
#include <gssapi/gssapi.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <mysql/plugin_auth.h>
 | 
						|
#include <mysqld_error.h>
 | 
						|
#include <string.h>
 | 
						|
#include "server_plugin.h"
 | 
						|
#include "gssapi_errmsg.h"
 | 
						|
 | 
						|
static gss_name_t service_name = GSS_C_NO_NAME;
 | 
						|
 | 
						|
/* This sends the error to the client */
 | 
						|
static void log_error( OM_uint32 major, OM_uint32 minor, const char *msg)
 | 
						|
{
 | 
						|
  if (GSS_ERROR(major))
 | 
						|
  {
 | 
						|
    char sysmsg[1024];
 | 
						|
    gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg));
 | 
						|
    my_printf_error(ER_UNKNOWN_ERROR,"Server GSSAPI error (major %u, minor %u) : %s -%s",
 | 
						|
      0, major, minor, msg, sysmsg);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    my_printf_error(ER_UNKNOWN_ERROR, "Server GSSAPI error : %s", 0, msg);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Generate default principal service name formatted as principal name "mariadb/server.fqdn@REALM"
 | 
						|
*/
 | 
						|
#include <krb5.h>
 | 
						|
#ifdef HAVE_KRB5_XFREE
 | 
						|
#define krb5_free_unparsed_name(a,b) krb5_xfree(b)
 | 
						|
#endif
 | 
						|
static char* get_default_principal_name()
 | 
						|
{
 | 
						|
  static char default_name[1024];
 | 
						|
  char *unparsed_name= NULL;
 | 
						|
  krb5_context context= NULL;
 | 
						|
  krb5_principal principal= NULL;
 | 
						|
  krb5_keyblock *key= NULL;
 | 
						|
 | 
						|
  if(krb5_init_context(&context))
 | 
						|
  {
 | 
						|
    my_printf_error(1, "GSSAPI plugin : krb5_init_context failed",
 | 
						|
                    ME_ERROR_LOG | ME_WARNING);
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  if (krb5_sname_to_principal(context, NULL, "mariadb", KRB5_NT_SRV_HST, &principal))
 | 
						|
  {
 | 
						|
    my_printf_error(1, "GSSAPI plugin :  krb5_sname_to_principal failed",
 | 
						|
                    ME_ERROR_LOG | ME_WARNING);
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  if (krb5_unparse_name(context, principal, &unparsed_name))
 | 
						|
  {
 | 
						|
    my_printf_error(1, "GSSAPI plugin :  krb5_unparse_name failed",
 | 
						|
                    ME_ERROR_LOG | ME_WARNING);
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check for entry in keytab */
 | 
						|
  if (krb5_kt_read_service_key(context, NULL, principal, 0, (krb5_enctype)0, &key))
 | 
						|
  {
 | 
						|
    my_printf_error(1, "GSSAPI plugin : default principal '%s' not found in keytab",
 | 
						|
                    ME_ERROR_LOG | ME_WARNING, unparsed_name);
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  strncpy(default_name, unparsed_name, sizeof(default_name)-1);
 | 
						|
 | 
						|
cleanup:
 | 
						|
  if (key)
 | 
						|
    krb5_free_keyblock(context, key);
 | 
						|
  if (unparsed_name)
 | 
						|
    krb5_free_unparsed_name(context, unparsed_name);
 | 
						|
  if (principal)
 | 
						|
    krb5_free_principal(context, principal);
 | 
						|
  if (context)
 | 
						|
    krb5_free_context(context);
 | 
						|
 | 
						|
  return default_name;
 | 
						|
}
 | 
						|
 | 
						|
static void release_service_name()
 | 
						|
{
 | 
						|
  if (service_name != GSS_C_NO_NAME)
 | 
						|
  {
 | 
						|
    OM_uint32 minor;
 | 
						|
    gss_release_name(&minor, &service_name);
 | 
						|
    service_name=  GSS_C_NO_NAME;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int plugin_init()
 | 
						|
{
 | 
						|
  gss_buffer_desc principal_name_buf;
 | 
						|
  OM_uint32 major= 0, minor= 0;
 | 
						|
  gss_cred_id_t cred= GSS_C_NO_CREDENTIAL;
 | 
						|
 | 
						|
  if(srv_keytab_path && srv_keytab_path[0])
 | 
						|
  {
 | 
						|
     setenv("KRB5_KTNAME", srv_keytab_path, 1);
 | 
						|
  }
 | 
						|
 | 
						|
  if(!srv_principal_name || !srv_principal_name[0])
 | 
						|
    srv_principal_name= get_default_principal_name();
 | 
						|
 | 
						|
  /* import service principal from plain text */
 | 
						|
  if(srv_principal_name && srv_principal_name[0])
 | 
						|
  {
 | 
						|
    my_printf_error(1, "GSSAPI plugin : using principal name '%s'",
 | 
						|
                    ME_ERROR_LOG | ME_NOTE, srv_principal_name);
 | 
						|
    principal_name_buf.length= strlen(srv_principal_name);
 | 
						|
    principal_name_buf.value= srv_principal_name;
 | 
						|
    major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name);
 | 
						|
    if(GSS_ERROR(major))
 | 
						|
    {
 | 
						|
      log_error(major, minor, "gss_import_name");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    service_name=  GSS_C_NO_NAME;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check if SPN configuration is OK */
 | 
						|
  major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE,
 | 
						|
                            GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL,
 | 
						|
                            NULL);
 | 
						|
 | 
						|
  if (GSS_ERROR(major))
 | 
						|
  {
 | 
						|
    log_error(major, minor, "gss_acquire_cred failed");
 | 
						|
    release_service_name();
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  gss_release_cred(&minor, &cred);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int plugin_deinit()
 | 
						|
{
 | 
						|
  release_service_name();
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int auth_server(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *auth_info)
 | 
						|
{
 | 
						|
 | 
						|
  int rc= CR_ERROR; /* return code */
 | 
						|
 | 
						|
  /* GSSAPI related fields */
 | 
						|
  OM_uint32 major= 0, minor= 0, flags= 0;
 | 
						|
  gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; /* credential identifier */
 | 
						|
  gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT; /* context identifier */
 | 
						|
  gss_name_t client_name;
 | 
						|
  gss_buffer_desc client_name_buf, input, output;
 | 
						|
  char *client_name_str;
 | 
						|
  const char *user= 0;
 | 
						|
  size_t userlen= 0;
 | 
						|
  int use_full_name= 0;
 | 
						|
 | 
						|
  /* server acquires credential */
 | 
						|
  major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE,
 | 
						|
                            GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL,
 | 
						|
                            NULL);
 | 
						|
 | 
						|
  if (GSS_ERROR(major))
 | 
						|
  {
 | 
						|
    log_error(major, minor, "gss_acquire_cred failed");
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  input.length= 0;
 | 
						|
  input.value= NULL;
 | 
						|
  do
 | 
						|
  {
 | 
						|
    /* receive token from peer */
 | 
						|
    int len= vio->read_packet(vio, (unsigned char **) &input.value);
 | 
						|
    if (len < 0)
 | 
						|
    {
 | 
						|
      log_error(0, 0, "fail to read token from client");
 | 
						|
      goto cleanup;
 | 
						|
    }
 | 
						|
    if (!user)
 | 
						|
    {
 | 
						|
      if (auth_info->auth_string_length > 0)
 | 
						|
      {
 | 
						|
        use_full_name= 1;
 | 
						|
        user= auth_info->auth_string;
 | 
						|
        userlen= auth_info->auth_string_length;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        use_full_name= 0;
 | 
						|
        user= auth_info->user_name;
 | 
						|
        userlen= auth_info->user_name_length;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    input.length= len;
 | 
						|
    major= gss_accept_sec_context(&minor, &ctxt, cred, &input,
 | 
						|
                                  GSS_C_NO_CHANNEL_BINDINGS, &client_name,
 | 
						|
                                  NULL, &output, &flags, NULL, NULL);
 | 
						|
    if (GSS_ERROR(major))
 | 
						|
    {
 | 
						|
 | 
						|
      log_error(major, minor, "gss_accept_sec_context");
 | 
						|
      rc= CR_ERROR;
 | 
						|
      goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    /* send token to peer */
 | 
						|
    if (output.length)
 | 
						|
    {
 | 
						|
      if (vio->write_packet(vio, (const unsigned char *) output.value, output.length))
 | 
						|
      {
 | 
						|
        gss_release_buffer(&minor, &output);
 | 
						|
        log_error(major, minor, "communication error(write)");
 | 
						|
        goto cleanup;
 | 
						|
      }
 | 
						|
      gss_release_buffer(&minor, &output);
 | 
						|
    }
 | 
						|
  } while (major & GSS_S_CONTINUE_NEEDED);
 | 
						|
 | 
						|
  /* extract plain text client name */
 | 
						|
  major= gss_display_name(&minor, client_name, &client_name_buf, NULL);
 | 
						|
  if (GSS_ERROR(major))
 | 
						|
  {
 | 
						|
    log_error(major, minor, "gss_display_name");
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  client_name_str= (char *)client_name_buf.value;
 | 
						|
 | 
						|
  /*
 | 
						|
   * Compare input user name with the actual one. Return success if
 | 
						|
   * the names match exactly, or if use_full_name parameter is not set
 | 
						|
   * up to the '@' separator.
 | 
						|
   */
 | 
						|
  if ((userlen == client_name_buf.length) ||
 | 
						|
      (!use_full_name
 | 
						|
       && userlen < client_name_buf.length
 | 
						|
       && client_name_str[userlen] == '@'))
 | 
						|
  {
 | 
						|
    if (user && strncmp(client_name_str, user, userlen) == 0)
 | 
						|
    {
 | 
						|
      rc= CR_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if(rc != CR_OK)
 | 
						|
  {
 | 
						|
    my_printf_error(ER_ACCESS_DENIED_ERROR,
 | 
						|
      "GSSAPI name mismatch, requested '%s', actual name '%.*s'",
 | 
						|
      0, user, (int)client_name_buf.length, client_name_str);
 | 
						|
  }
 | 
						|
 | 
						|
  gss_release_buffer(&minor, &client_name_buf);
 | 
						|
 | 
						|
 | 
						|
cleanup:
 | 
						|
  if (ctxt != GSS_C_NO_CONTEXT)
 | 
						|
    gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);
 | 
						|
  if (cred != GSS_C_NO_CREDENTIAL)
 | 
						|
    gss_release_cred(&minor, &cred);
 | 
						|
 | 
						|
  return(rc);
 | 
						|
}
 |