Ejemplo n.º 1
0
/* 
 SSL error handling
*/
static void my_SSL_error(MYSQL *mysql)
{
  ulong ssl_errno= ERR_get_error();
  char  ssl_error[MAX_SSL_ERR_LEN];
  const char *ssl_error_reason;

  DBUG_ENTER("my_SSL_error");

  if (mysql_errno(mysql))
    DBUG_VOID_RETURN;

  if (!ssl_errno)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
    DBUG_VOID_RETURN;
  }
  if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 
                 ER(CR_SSL_CONNECTION_ERROR), ssl_error_reason);
    DBUG_VOID_RETURN;
  }
  my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
  my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 
               ER(CR_SSL_CONNECTION_ERROR), ssl_error);
  DBUG_VOID_RETURN;
}
Ejemplo n.º 2
0
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len)
{
  MYSQL *mysql;
  size_t fp_len= len;
  const gnutls_datum_t *cert_list;
  unsigned int cert_list_size;

  if (!cssl || !cssl->ssl)
    return 0;

  mysql= (MYSQL *)gnutls_session_get_ptr(cssl->ssl);

  cert_list = gnutls_certificate_get_peers (cssl->ssl, &cert_list_size);
  if (cert_list == NULL)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Unable to get server certificate");
    return 0;
  }

  if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0)
    return fp_len;
  else
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Finger print buffer too small");
    return 0;
  }
}
Ejemplo n.º 3
0
/*
  establish SSL connection between client 
  and server

  SYNOPSIS
    my_ssl_connect
      ssl      ssl object

  RETURN VALUES
    0  success
    1  error
*/
int my_ssl_connect(SSL *ssl)
{
  my_bool blocking;
  MYSQL *mysql;
  long rc;
  my_bool try_connect= 1;

  DBUG_ENTER("my_ssl_connect");

  DBUG_ASSERT(ssl != NULL);

  mysql= (MYSQL *)SSL_get_app_data(ssl);
  CLEAR_CLIENT_ERROR(mysql);

  /* Set socket to non blocking */
  if (!(blocking= vio_is_blocking(mysql->net.vio)))
    vio_blocking(mysql->net.vio, FALSE, 0);

  SSL_clear(ssl);
  SSL_SESSION_set_timeout(SSL_get_session(ssl),
                          mysql->options.connect_timeout);
  SSL_set_fd(ssl, mysql->net.vio->sd);

  while (try_connect && (rc= SSL_connect(ssl)) == -1)
  {
    switch(SSL_get_error(ssl, rc)) {
    case SSL_ERROR_WANT_READ:
      if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1)
        try_connect= 0;
      break;
    case SSL_ERROR_WANT_WRITE:
      if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1)
        try_connect= 0;
    break;
    default:
      try_connect= 0;
    }
  }
  if (rc != 1)
  {
    my_SSL_error(mysql);
    DBUG_RETURN(1);
  }

  rc= SSL_get_verify_result(ssl);
  if (rc != X509_V_OK)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 
                 ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
    /* restore blocking mode */
    if (!blocking)
      vio_blocking(mysql->net.vio, FALSE, 0);

    DBUG_RETURN(1);
  }

  vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0);
  mysql->net.vio->ssl= ssl;
  DBUG_RETURN(0);
}
Ejemplo n.º 4
0
static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
                                     const uchar *pkt, size_t pkt_len)
{
    int res;
    MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;

    if (mpvio->packets_written == 0)
    {
        if (mpvio->mysql_change_user)
            res= send_change_user_packet(mpvio, pkt, (int)pkt_len);
        else
            res= send_client_reply_packet(mpvio, pkt, (int)pkt_len);
    }
    else
    {
        NET *net= &mpvio->mysql->net;
        if (mpvio->mysql->thd)
            res= 1; /* no chit-chat in embedded */
        else
            res= my_net_write(net, (char *)pkt, pkt_len) || net_flush(net);
        if (res)
            my_set_error(mpvio->mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
                         ER(CR_SERVER_LOST_EXTENDED),
                         "sending authentication information",
                         errno);
    }
    mpvio->packets_written++;
    return res;
}
Ejemplo n.º 5
0
int my_ssl_verify_server_cert(SSL *ssl)
{
  X509 *cert;
  MYSQL *mysql;
  char *p1, *p2, buf[256];

  DBUG_ENTER("my_ssl_verify_server_cert");

  mysql= (MYSQL *)SSL_get_app_data(ssl);

  if (!mysql->host)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Invalid (empty) hostname");
    DBUG_RETURN(1);
  }

  if (!(cert= SSL_get_peer_certificate(ssl)))
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Unable to get server certificate");
    DBUG_RETURN(1);
  }

  X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
  X509_free(cert);

  /* Extract the server name from buffer:
     Format: ....CN=/hostname/.... */
  if ((p1= strstr(buf, "/CN=")))
  {
    p1+= 4;
    if ((p2= strchr(p1, '/')))
      *p2= 0;
    if (!strcmp(mysql->host, p1))
      DBUG_RETURN(0);
  }
  my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                      ER(CR_SSL_CONNECTION_ERROR), 
                      "Validation of SSL server certificate failed");
  DBUG_RETURN(1);
}
Ejemplo n.º 6
0
static int send_change_user_packet(MCPVIO_EXT *mpvio,
                                   const uchar *data, int data_len)
{
    MYSQL *mysql= mpvio->mysql;
    char *buff, *end;
    int res= 1;
    size_t conn_attr_len= (mysql->options.extension) ?
                          mysql->options.extension->connect_attrs_len : 0;

    buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1 + 9 + conn_attr_len);

    end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;

    if (!data_len)
        *end++= 0;
    else
    {
        if (mysql->client_flag & CLIENT_SECURE_CONNECTION)
        {
            DBUG_ASSERT(data_len <= 255);
            if (data_len > 255)
            {
                my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
                goto error;
            }
            *end++= data_len;
        }
        else
        {
            DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1);
            DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0);
        }
        memcpy(end, data, data_len);
        end+= data_len;
    }
    end= strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;

    if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
    {
        int2store(end, (ushort) mysql->charset->nr);
        end+= 2;
    }

    if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
        end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;

    end= ma_send_connect_attr(mysql, end);

    res= simple_command(mysql, MYSQL_COM_CHANGE_USER,
                        buff, (ulong)(end-buff), 1, NULL);

error:
    my_afree(buff);
    return res;
}
Ejemplo n.º 7
0
my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
{
  unsigned int cert_fp_len= 64;
  char *cert_fp= NULL;
  my_bool rc=1;
  MYSQL *mysql= ctls->pvio->mysql;

  cert_fp= (char *)malloc(cert_fp_len);

  if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
    goto end;
  if (fp)
    rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
  else if (fp_list)
  {
    MA_FILE *fp;
    char buff[255];

    if (!(fp = ma_open(fp_list, "r", mysql)))
      goto end;

    while (ma_gets(buff, sizeof(buff)-1, fp))
    {
      /* remove trailing new line character */
      char *pos= strchr(buff, '\r');
      if (!pos)
        pos= strchr(buff, '\n');
      if (pos)
        *pos= '\0';
        
      if (!ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
      {
        /* finger print is valid: close file and exit */
        ma_close(fp);
        rc= 0;
        goto end;
      }
    }

    /* No finger print matched - close file and return error */
    ma_close(fp);
  }

end:
  if (cert_fp)
    free(cert_fp);
  if (rc)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                         ER(CR_SSL_CONNECTION_ERROR), 
                         "Fingerprint verification of server certificate failed");
  }
  return rc;
}
Ejemplo n.º 8
0
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, unsigned char *fp, unsigned int len)
{
  EVP_MD *digest= (EVP_MD *)EVP_sha1();
  X509 *cert;
  MYSQL *mysql;
  unsigned int fp_len;

  if (!ctls || !ctls->ssl)
    return 0;

  mysql= SSL_get_app_data(ctls->ssl);

  if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Unable to get server certificate");
    return 0;
  }

  if (len < EVP_MAX_MD_SIZE)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Finger print buffer too small");
    return 0;
  }
  fp_len= len;
  if (!X509_digest(cert, digest, fp, &fp_len))
  {
    ma_free(fp);
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "invalid finger print of server certificate");
    return 0;
  }
  return (fp_len);
}
Ejemplo n.º 9
0
/*
  establish SSL connection between client
  and server

  SYNOPSIS
    my_ssl_connect
      ssl      ssl object

  RETURN VALUES
    0  success
    1  error
*/
int my_ssl_connect(SSL *ssl)
{
    my_bool blocking;
    MYSQL *mysql;
    long rc;

    DBUG_ENTER("my_ssl_connect");

    DBUG_ASSERT(ssl != NULL);

    mysql= (MYSQL *)SSL_get_app_data(ssl);
    CLEAR_CLIENT_ERROR(mysql);

    /* Set socket to blocking if not already set */
    if (!(blocking= vio_is_blocking(mysql->net.vio)))
        vio_blocking(mysql->net.vio, TRUE, 0);

    SSL_clear(ssl);
    SSL_SESSION_set_timeout(SSL_get_session(ssl),
                            mysql->options.connect_timeout);
    SSL_set_fd(ssl, mysql->net.vio->sd);

    if (SSL_connect(ssl) != 1)
    {
        my_SSL_error(mysql);
        /* restore blocking mode */
        if (!blocking)
            vio_blocking(mysql->net.vio, FALSE, 0);
        DBUG_RETURN(1);
    }

    rc= SSL_get_verify_result(ssl);
    if (rc != X509_V_OK)
    {
        my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                     ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
        /* restore blocking mode */
        if (!blocking)
            vio_blocking(mysql->net.vio, FALSE, 0);

        DBUG_RETURN(1);
    }

    vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0);
    mysql->net.vio->ssl= ssl;
    DBUG_RETURN(0);
}
Ejemplo n.º 10
0
my_bool ma_tls_connect(MARIADB_TLS *ctls)
{
  SSL *ssl = (SSL *)ctls->ssl;
  my_bool blocking;
  MYSQL *mysql;
  MARIADB_PVIO *pvio;
  int rc;

  mysql= (MYSQL *)SSL_get_app_data(ssl);
  pvio= mysql->net.pvio;

  /* Set socket to blocking if not already set */
  if (!(blocking= pvio->methods->is_blocking(pvio)))
    pvio->methods->blocking(pvio, TRUE, 0);

  SSL_clear(ssl);
  SSL_SESSION_set_timeout(SSL_get_session(ssl),
                          mysql->options.connect_timeout);
  SSL_set_fd(ssl, mysql_get_socket(mysql));

  if (SSL_connect(ssl) != 1)
  {
    ma_tls_set_error(mysql);
    /* restore blocking mode */
    if (!blocking)
      pvio->methods->blocking(pvio, FALSE, 0);
    return 1;
  }
  if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
  {
    rc= SSL_get_verify_result(ssl);
    if (rc != X509_V_OK)
    {
      my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 
                   ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
      /* restore blocking mode */
      if (!blocking)
        pvio->methods->blocking(pvio, FALSE, 0);

      return 1;
    }
  }
  pvio->ctls->ssl= ctls->ssl= (void *)ssl;

  return 0;
}
Ejemplo n.º 11
0
static int my_verify_callback(int ok, X509_STORE_CTX *ctx)
{
  X509 *check_cert;
  SSL *ssl;
  MYSQL *mysql;
  DBUG_ENTER("my_verify_callback");

  ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
  DBUG_ASSERT(ssl != NULL);
  mysql= (MYSQL *)SSL_get_app_data(ssl);
  DBUG_ASSERT(mysql != NULL);

  /* skip verification if no ca_file/path was specified */
  if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
  {
    ok= 1;
    DBUG_RETURN(1);
  }

  if (!ok)
  {
    uint depth;
    if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx)))
      DBUG_RETURN(0);
    depth= X509_STORE_CTX_get_error_depth(ctx);
    if (depth == 0)
    {
      ok= 1;
      DBUG_RETURN(1);
    }
  }
  else
    DBUG_RETURN(1);

  my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                      ER(CR_SSL_CONNECTION_ERROR), 
                      X509_verify_cert_error_string(ctx->error));
  DBUG_RETURN(0);
}
Ejemplo n.º 12
0
int my_ssl_verify_server_cert(SSL *ssl)
{
  X509 *cert;
  MYSQL *mysql;
  X509_NAME *x509sn;
  int cn_pos;
  X509_NAME_ENTRY *cn_entry;
  ASN1_STRING *cn_asn1;
  const char *cn_str;

  DBUG_ENTER("my_ssl_verify_server_cert");

  mysql= (MYSQL *)SSL_get_app_data(ssl);

  if (!mysql->host)
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Invalid (empty) hostname");
    DBUG_RETURN(1);
  }

  if (!(cert= SSL_get_peer_certificate(ssl)))
  {
    my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                        ER(CR_SSL_CONNECTION_ERROR), 
                        "Unable to get server certificate");
    DBUG_RETURN(1);
  }

  x509sn= X509_get_subject_name(cert);

  if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0)
    goto error;

  if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos)))
    goto error;

  if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry)))
    goto error;

  cn_str = (char *)ASN1_STRING_data(cn_asn1);

  /* Make sure there is no embedded \0 in the CN */
  if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str))
    goto error;

  if (strcmp(cn_str, mysql->host))
    goto error;

  X509_free(cert);

  DBUG_RETURN(0);

error:
  X509_free(cert);

  my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                      ER(CR_SSL_CONNECTION_ERROR), 
                      "Validation of SSL server certificate failed");
  DBUG_RETURN(1);
}
Ejemplo n.º 13
0
static int send_client_reply_packet(MCPVIO_EXT *mpvio,
                                    const uchar *data, int data_len)
{
  MYSQL *mysql= mpvio->mysql;
  NET *net= &mysql->net;
  char *buff, *end;
  size_t conn_attr_len= (mysql->options.extension) ? 
                         mysql->options.extension->connect_attrs_len : 0;

  /* see end= buff+32 below, fixed size of the packet is 32 bytes */
  buff= malloc(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9);
  end= buff;

  mysql->client_flag|= mysql->options.client_flag;
  mysql->client_flag|= CLIENT_CAPABILITIES;

  if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
    mysql->client_flag|= CLIENT_MULTI_RESULTS;

#if defined(HAVE_TLS) && !defined(EMBEDDED_LIBRARY)
  if (mysql->options.ssl_key || mysql->options.ssl_cert ||
      mysql->options.ssl_ca || mysql->options.ssl_capath ||
      mysql->options.ssl_cipher || mysql->options.use_ssl ||
      (mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
    mysql->options.use_ssl= 1;
  if (mysql->options.use_ssl)
    mysql->client_flag|= CLIENT_SSL;
#endif /* HAVE_TLS && !EMBEDDED_LIBRARY*/
  if (mpvio->db)
    mysql->client_flag|= CLIENT_CONNECT_WITH_DB;

  /* if server doesn't support SSL and verification of server certificate
     was set to mandatory, we need to return an error */
  if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL))
  {
    if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
        (mysql->options.extension && (mysql->options.extension->tls_fp || 
                                      mysql->options.extension->tls_fp_list)))
    {
      my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                          ER(CR_SSL_CONNECTION_ERROR), 
                          "SSL is required, but the server does not support it");
      goto error;
    }
  }


  /* Remove options that server doesn't support */
  mysql->client_flag= mysql->client_flag &
                       (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) 
                       | mysql->server_capabilities);

#ifndef HAVE_COMPRESS
  mysql->client_flag&= ~CLIENT_COMPRESS;
#endif

  if (mysql->client_flag & CLIENT_PROTOCOL_41)
  {
    /* 4.1 server and 4.1 client has a 32 byte option flag */
    if (!(mysql->server_capabilities & CLIENT_MYSQL))
      mysql->client_flag&= ~CLIENT_MYSQL;
    int4store(buff,mysql->client_flag);
    int4store(buff+4, net->max_packet_size);
    buff[8]= (char) mysql->charset->nr;
    memset(buff + 9, 0, 32-9);
    if (!(mysql->server_capabilities & CLIENT_MYSQL))
    {
      mysql->extension->mariadb_client_flag = MARIADB_CLIENT_SUPPORTED_FLAGS >> 32;
      int4store(buff + 28, mysql->extension->mariadb_client_flag);
    }
    end= buff+32;
  }
Ejemplo n.º 14
0
static int send_client_reply_packet(MCPVIO_EXT *mpvio,
                                    const uchar *data, int data_len)
{
    MYSQL *mysql= mpvio->mysql;
    NET *net= &mysql->net;
    char *buff, *end;
    size_t conn_attr_len= (mysql->options.extension) ?
                          mysql->options.extension->connect_attrs_len : 0;

    /* see end= buff+32 below, fixed size of the packet is 32 bytes */
    buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9);

    mysql->client_flag|= mysql->options.client_flag;
    mysql->client_flag|= CLIENT_CAPABILITIES;

    if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
        mysql->client_flag|= CLIENT_MULTI_RESULTS;

#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
    if (mysql->options.ssl_key || mysql->options.ssl_cert ||
            mysql->options.ssl_ca || mysql->options.ssl_capath ||
            mysql->options.ssl_cipher)
        mysql->options.use_ssl= 1;
    if (mysql->options.use_ssl)
        mysql->client_flag|= CLIENT_SSL;

    /* if server doesn't support SSL and verification of server certificate
       was set to mandatory, we need to return an error */
    if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL))
    {
        if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
                (mysql->options.extension && (mysql->options.extension->ssl_fp ||
                                              mysql->options.extension->ssl_fp_list)))
        {
            my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                         ER(CR_SSL_CONNECTION_ERROR),
                         "Server doesn't support SSL");
            goto error;
        }
    }

#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
    if (mpvio->db)
        mysql->client_flag|= CLIENT_CONNECT_WITH_DB;

    /* Remove options that server doesn't support */
    mysql->client_flag= mysql->client_flag &
                        (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)
                         | mysql->server_capabilities);

#ifndef HAVE_COMPRESS
    mysql->client_flag&= ~CLIENT_COMPRESS;
#endif

    if (mysql->client_flag & CLIENT_PROTOCOL_41)
    {
        /* 4.1 server and 4.1 client has a 32 byte option flag */
        int4store(buff,mysql->client_flag);
        int4store(buff+4, net->max_packet_size);
        buff[8]= (char) mysql->charset->nr;
        bzero(buff+9, 32-9);
        end= buff+32;
    }
    else
    {
        int2store(buff, mysql->client_flag);
        int3store(buff+2, net->max_packet_size);
        end= buff+5;
    }
#ifdef HAVE_OPENSSL
    if (mysql->options.ssl_key ||
            mysql->options.ssl_cert ||
            mysql->options.ssl_ca ||
            mysql->options.ssl_capath ||
            mysql->options.ssl_cipher
#ifdef CRL_IMPLEMENTED
            || (mysql->options.extension &&
                (mysql->options.extension->ssl_crl ||
                 mysql->options.extension->ssl_crlpath))
#endif
       )
        mysql->options.use_ssl= 1;

    if (mysql->options.use_ssl &&
            (mysql->client_flag & CLIENT_SSL))
    {
        SSL *ssl;
        /*
          Send mysql->client_flag, max_packet_size - unencrypted otherwise
          the server does not know we want to do SSL
        */
        if (my_net_write(net, (char*)buff, (size_t) (end-buff)) || net_flush(net))
        {
            my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
                         ER(CR_SERVER_LOST_EXTENDED),
                         "sending connection information to server",
                         errno);
            goto error;
        }

        /* Create SSL */
        if (!(ssl= my_ssl_init(mysql)))
            goto error;

        /* Connect to the server */
        if (my_ssl_connect(ssl))
        {
            SSL_free(ssl);
            goto error;
        }

        if (mysql->options.extension &&
                (mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list))
        {
            if (ma_ssl_verify_fingerprint(ssl))
                goto error;
        }

        if ((mysql->options.ssl_ca || mysql->options.ssl_capath) &&
                (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
                my_ssl_verify_server_cert(ssl))
            goto error;
    }
#endif /* HAVE_OPENSSL */

    DBUG_PRINT("info",("Server version = '%s'  capabilites: %lu  status: %u  client_flag: %lu",
                       mysql->server_version, mysql->server_capabilities,
                       mysql->server_status, mysql->client_flag));

    compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);

    /* This needs to be changed as it's not useful with big packets */
    if (mysql->user[0])
        strmake(end, mysql->user, USERNAME_LENGTH);
    else
        read_user_name(end);

    /* We have to handle different version of handshake here */
    DBUG_PRINT("info",("user: %s",end));
    end= strend(end) + 1;
    if (data_len)
    {
        if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
        {
            *end++= data_len;
            memcpy(end, data, data_len);
            end+= data_len;
        }
        else
        {
            DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */
            memcpy(end, data, data_len);
            end+= data_len;
        }
    }
    else
        *end++= 0;

    /* Add database if needed */
    if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
    {
        end= strmake(end, mpvio->db, NAME_LEN) + 1;
        mysql->db= my_strdup(mpvio->db, MYF(MY_WME));
    }

    if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
        end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;

    end= ma_send_connect_attr(mysql, end);

    /* Write authentication package */
    if (my_net_write(net, buff, (size_t) (end-buff)) || net_flush(net))
    {
        my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
                     ER(CR_SERVER_LOST_EXTENDED),
                     "sending authentication information",
                     errno);
        goto error;
    }
    my_afree(buff);
    return 0;

error:
    my_afree(buff);
    return 1;
}
Ejemplo n.º 15
0
int ma_ssl_verify_fingerprint(SSL *ssl)
{
    X509 *cert= SSL_get_peer_certificate(ssl);
    MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl);
    unsigned char fingerprint[EVP_MAX_MD_SIZE];
    EVP_MD *digest;
    unsigned int fp_length;

    DBUG_ENTER("ma_ssl_verify_fingerprint");

    if (!cert)
    {
        my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                     ER(CR_SSL_CONNECTION_ERROR),
                     "Unable to get server certificate");
        DBUG_RETURN(1);
    }

    digest= (EVP_MD *)EVP_sha1();
    fp_length= sizeof(fingerprint);

    if (!ma_get_cert_fingerprint(cert, digest, fingerprint, &fp_length))
    {
        my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                     ER(CR_SSL_CONNECTION_ERROR),
                     "Unable to get finger print of server certificate");
        DBUG_RETURN(1);
    }

    /* single finger print was specified */
    if (mysql->options.extension->ssl_fp)
    {
        if (ma_check_fingerprint(fingerprint, fp_length, mysql->options.extension->ssl_fp,
                                 strlen(mysql->options.extension->ssl_fp)))
        {
            my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                         ER(CR_SSL_CONNECTION_ERROR),
                         "invalid finger print of server certificate");
            DBUG_RETURN(1);
        }
    }

    /* white list of finger prints was specified */
    if (mysql->options.extension->ssl_fp_list)
    {
        FILE *fp;
        char buff[255];

        if (!(fp = my_fopen(mysql->options.extension->ssl_fp_list ,O_RDONLY, MYF(0))))
        {
            my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                         ER(CR_SSL_CONNECTION_ERROR),
                         "Can't open finger print list");
            DBUG_RETURN(1);
        }

        while (fgets(buff, sizeof(buff)-1, fp))
        {
            /* remove trailing new line character */
            char *pos= strchr(buff, '\r');
            if (!pos)
                pos= strchr(buff, '\n');
            if (pos)
                *pos= '\0';

            if (!ma_check_fingerprint(fingerprint, fp_length, buff, strlen(buff)))
            {
                /* finger print is valid: close file and exit */
                my_fclose(fp, MYF(0));
                DBUG_RETURN(0);
            }
        }

        /* No finger print matched - close file and return error */
        my_fclose(fp, MYF(0));
        my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                     ER(CR_SSL_CONNECTION_ERROR),
                     "invalid finger print of server certificate");
        DBUG_RETURN(1);
    }
    DBUG_RETURN(0);
}
/* {{{ mysql_handle_local_infile */
my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename)
{
  unsigned int buflen= 4096;
  int bufread;
  unsigned char *buf= NULL;
  void *info= NULL;
  my_bool result= 1;

  /* check if all callback functions exist */
  if (!conn->options.local_infile_init || !conn->options.local_infile_end ||
      !conn->options.local_infile_read || !conn->options.local_infile_error)
  {
    conn->options.local_infile_userdata= conn;
    mysql_set_local_infile_default(conn);
  }

  if (!(conn->options.client_flag & CLIENT_LOCAL_FILES)) {
    my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden");
    /* write empty packet to server */
    ma_net_write(&conn->net, (unsigned char *)"", 0);
    ma_net_flush(&conn->net);
    goto infile_error;
  }

  /* allocate buffer for reading data */
  buf = (uchar *)malloc(buflen);

  /* init handler: allocate read buffer and open file */
  if (conn->options.local_infile_init(&info, filename,
                                      conn->options.local_infile_userdata))
  {
    char tmp_buf[MYSQL_ERRMSG_SIZE];
    int tmp_errno;

    tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
    my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
    ma_net_write(&conn->net, (unsigned char *)"", 0);
    ma_net_flush(&conn->net);
    goto infile_error;
  }

  /* read data */
  while ((bufread= conn->options.local_infile_read(info, (char *)buf, buflen)) > 0)
  {
    if (ma_net_write(&conn->net, (unsigned char *)buf, bufread))
    {
      my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
      goto infile_error;
    }
  }

  /* send empty packet for eof */
  if (ma_net_write(&conn->net, (unsigned char *)"", 0) || 
      ma_net_flush(&conn->net))
  {
    my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
    goto infile_error;
  }

  /* error during read occurred */
  if (bufread < 0)
  {
    char tmp_buf[MYSQL_ERRMSG_SIZE];
    int tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
    my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
    goto infile_error;
  }

  result = 0;

infile_error:
  conn->options.local_infile_end(info);
  free(buf);
  return(result);
}
Ejemplo n.º 17
0
my_bool ma_tls_connect(MARIADB_TLS *ctls)
{
  SSL *ssl = (SSL *)ctls->ssl;
  my_bool blocking, try_connect= 1;
  MYSQL *mysql;
  MARIADB_PVIO *pvio;
  int rc;
#if OPENSSL_USE_BIOMETHOD
  BIO_METHOD *bio_method= NULL;
  BIO *bio;
#endif

  mysql= (MYSQL *)SSL_get_app_data(ssl);
  pvio= mysql->net.pvio;

  /* Set socket to non blocking if not already set */
  if (!(blocking= pvio->methods->is_blocking(pvio)))
    pvio->methods->blocking(pvio, FALSE, 0);

  SSL_clear(ssl);

#if OPENSSL_USE_BIOMETHOD
  bio= BIO_new(&ma_BIO_method);
  bio->ptr= pvio;
  SSL_set_bio(ssl, bio, bio);
  BIO_set_fd(bio, mysql_get_socket(mysql), BIO_NOCLOSE);
#else
  SSL_set_fd(ssl, mysql_get_socket(mysql));
#endif

  while (try_connect && (rc= SSL_connect(ssl)) == -1)
  {
    switch(SSL_get_error(ssl, rc)) {
    case SSL_ERROR_WANT_READ:
      if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
        try_connect= 0;
      break;
    case SSL_ERROR_WANT_WRITE:
      if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
        try_connect= 0;
      break;
    default:
      try_connect= 0;
    }
  }
  if (rc != 1)
  {
    ma_tls_set_error(mysql);
    /* restore blocking mode */
    if (!blocking)
      pvio->methods->blocking(pvio, FALSE, 0);
    return 1;
  }
  if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
  {
    rc= SSL_get_verify_result(ssl);
    if (rc != X509_V_OK)
    {
      my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 
                   ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
      /* restore blocking mode */
      if (!blocking)
        pvio->methods->blocking(pvio, FALSE, 0);

      return 1;
    }
  }
  pvio->ctls->ssl= ctls->ssl= (void *)ssl;

  return 0;
}
Ejemplo n.º 18
0
/* {{{ ma_open */
MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql)
{
  int CodePage= -1;
  FILE *fp= NULL;
  MA_FILE *ma_file= NULL;

  if (!location || !location[0])
    return NULL;
#ifdef HAVE_REMOTEIO
  if (strstr(location, "://"))
    goto remote;
#endif

#ifdef _WIN32
  if (mysql && mysql->charset)
    CodePage= madb_get_windows_cp(mysql->charset->csname);
#endif
  if (CodePage == -1)
  {
    if (!(fp= fopen(location, mode)))
    {
#ifdef _WIN32
      my_errno= GetLastError();
#else
      my_errno= errno;
#endif
      return NULL;
    }
  }
#ifdef _WIN32
  /* See CONC-44: we need to support non ascii filenames too, so we convert
     current character set to wchar_t and try to open the file via _wsopen */
  else
   {
    wchar_t *w_filename= NULL;
    wchar_t *w_mode= NULL;
    int len;
    DWORD Length;

    len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), NULL, 0);
    if (!len)
      return NULL;
    if (!(w_filename= (wchar_t *)my_malloc((len + 1) * sizeof(wchar_t), MYF(MY_ZEROFILL))))
    {
      my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
      return NULL;
    }
    Length= len;
    len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), w_filename, (int)Length);
    if (!len)
    {
      /* todo: error handling */
      my_free(w_filename);
      return NULL;
    }
    len= (int)strlen(mode);
    if (!(w_mode= (wchar_t *)my_malloc((len + 1) * sizeof(wchar_t), MYF(MY_ZEROFILL))))
    {
      my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
      my_free(w_filename);
      return NULL;
    }
    Length= len;
    len= MultiByteToWideChar(CodePage, 0, mode, (int)strlen(mode), w_mode, (int)Length);
    if (!len)
    {
      /* todo: error handling */
      my_free(w_filename);
      my_free(w_mode);
      return NULL;
    }
    fp= _wfopen(w_filename, w_mode);
    my_errno= GetLastError();
    my_free(w_filename);
    my_free(w_mode);
  }

#endif
  if (fp)
  {
    ma_file= (MA_FILE *)my_malloc(sizeof(MA_FILE), MYF(0));
    if (!ma_file)
    {
      my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
      return NULL;
    }
    ma_file->type= MA_FILE_LOCAL;
    ma_file->ptr= (void *)fp;
  }
  return ma_file;
#ifdef HAVE_REMOTEIO
remote:
  /* check if plugin for remote io is available and try
   * to open location */
  {
    MYSQL mysql;
    if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *)
                      mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN)))
      return rio_plugin->methods->open(location, mode);
    return NULL;
  }
#endif
}
Ejemplo n.º 19
0
int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
                    const char *data_plugin, const char *db)
{
    const char    *auth_plugin_name;
    auth_plugin_t *auth_plugin;
    MCPVIO_EXT    mpvio;
    ulong		pkt_length;
    int           res;

    /* determine the default/initial plugin to use */
    if (mysql->options.extension && mysql->options.extension->default_auth &&
            mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
    {
        auth_plugin_name= mysql->options.extension->default_auth;
        if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql,
                           auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
            return 1; /* oops, not found */
    }
    else
    {
        auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ?
                     &native_password_client_plugin : &old_password_client_plugin;
        auth_plugin_name= auth_plugin->name;
    }

    mysql->net.last_errno= 0; /* just in case */

    if (data_plugin && strcmp(data_plugin, auth_plugin_name))
    {
        /* data was prepared for a different plugin, don't show it to this one */
        data= 0;
        data_len= 0;
    }

    mpvio.mysql_change_user= data_plugin == 0;
    mpvio.cached_server_reply.pkt= (uchar*)data;
    mpvio.cached_server_reply.pkt_len= data_len;
    mpvio.read_packet= client_mpvio_read_packet;
    mpvio.write_packet= client_mpvio_write_packet;
    mpvio.info= client_mpvio_info;
    mpvio.mysql= mysql;
    mpvio.packets_read= mpvio.packets_written= 0;
    mpvio.db= db;
    mpvio.plugin= auth_plugin;

    res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);

    compile_time_assert(CR_OK == -1);
    compile_time_assert(CR_ERROR == 0);
    if (res > CR_OK && mysql->net.read_pos[0] != 254)
    {
        /*
          the plugin returned an error. write it down in mysql,
          unless the error code is CR_ERROR and mysql->net.last_errno
          is already set (the plugin has done it)
        */
        if (res > CR_ERROR)
            my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0);
        else if (!mysql->net.last_errno)
            my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
        return 1;
    }

    /* read the OK packet (or use the cached value in mysql->net.read_pos */
    if (res == CR_OK)
        pkt_length= net_safe_read(mysql);
    else /* res == CR_OK_HANDSHAKE_COMPLETE */
        pkt_length= mpvio.last_read_packet_len;

    if (pkt_length == packet_error)
    {
        if (mysql->net.last_errno == CR_SERVER_LOST)
            my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
                         ER(CR_SERVER_LOST_EXTENDED),
                         "reading authorization packet",
                         errno);
        return 1;
    }

    if (mysql->net.read_pos[0] == 254)
    {
        /* The server asked to use a different authentication plugin */
        if (pkt_length == 1)
        {
            /* old "use short scramble" packet */
            auth_plugin_name= old_password_plugin_name;
            mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble_buff;
            mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1;
        }
        else
        {
            /* new "use different plugin" packet */
            uint len;
            auth_plugin_name= (char*)mysql->net.read_pos + 1;
            len= (uint)strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */
            mpvio.cached_server_reply.pkt_len= pkt_length - len - 2;
            mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2;
        }

        if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql,
                           auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
            return 1;

        mpvio.plugin= auth_plugin;
        res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);

        if (res > CR_OK)
        {
            if (res > CR_ERROR)
                my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0);
            else if (!mysql->net.last_errno)
                my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
            return 1;
        }

        if (res != CR_OK_HANDSHAKE_COMPLETE)
        {
            /* Read what server thinks about out new auth message report */
            if (net_safe_read(mysql) == packet_error)
            {
                if (mysql->net.last_errno == CR_SERVER_LOST)
                    my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
                                 ER(CR_SERVER_LOST_EXTENDED),
                                 "reading final connect information",
                                 errno);
                return 1;
            }
        }
    }
    /*
      net->read_pos[0] should always be 0 here if the server implements
      the protocol correctly
    */
    return mysql->net.read_pos[0] != 0;
}