示例#1
0
my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags)
{
  SECURITY_STATUS sRet;
  DWORD flags;
  MARIADB_PVIO *pvio= sctx->mysql->net.pvio;
  PCCERT_CONTEXT pServerCert= NULL;

  if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
  {
    ma_schannel_set_sec_error(pvio, sRet);
    return 0;
  }

  flags= CERT_STORE_SIGNATURE_FLAG |
         CERT_STORE_TIME_VALIDITY_FLAG;


    
  if (sctx->client_ca_ctx)
  {
 	  if (!(sRet= CertVerifySubjectCertificateContext(pServerCert,
                                                    sctx->client_ca_ctx,
                                                    &flags)))
    {
      ma_schannel_set_win_error(pvio);
      return 0;
    }

    if (flags)
    {
      if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
        pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Certificate signature check failed");
      else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
        pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "certificate was revoked");
      else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
        pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "certificate has expired");
      else
        pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown error during certificate validation");
      return 0;
    }
  }

  /* Check if none of the certificates in the certificate chain have been revoked. */
  if (sctx->client_crl_ctx)
  {
    PCRL_INFO Info[1];

    Info[0]= sctx->client_crl_ctx->pCrlInfo;
    if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                                  pServerCert->pCertInfo,
                                  1, Info))                               )
    {
      pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed");
      return 0;
    }
  }
  return 1;
}
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
                                         PCredHandle phCreds,
                                         CtxtHandle * phContext,
                                         DWORD *DecryptLength,
                                         uchar *ReadBuffer,
                                         DWORD ReadBufferSize)
{
  DWORD dwBytesRead= 0;
  DWORD dwOffset= 0;
  SC_CTX *sctx;
  SECURITY_STATUS sRet= 0;
  SecBufferDesc Msg;
  SecBuffer Buffers[4],
            *pData, *pExtra;
  int i;

  if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
    return SEC_E_INTERNAL_ERROR;

  sctx= (SC_CTX *)pvio->ctls->ssl;
  *DecryptLength= 0;

  while (1)
  {
    if (!dwBytesRead || sRet == SEC_E_INCOMPLETE_MESSAGE)
    {
      dwBytesRead= (DWORD)pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
      if (dwBytesRead == 0)
      {
        /* server closed connection */
        // todo: error 
        return SEC_E_INVALID_HANDLE;
      }
      if (dwBytesRead < 0)
      {
        /* socket error */
        // todo: error
        return SEC_E_INVALID_HANDLE;
      }
      dwOffset+= dwBytesRead;
    }
    ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
    Buffers[0].pvBuffer= sctx->IoBuffer;
    Buffers[0].cbBuffer= dwOffset;

    Buffers[0].BufferType= SECBUFFER_DATA; 
    Buffers[1].BufferType=
    Buffers[2].BufferType=
    Buffers[3].BufferType= SECBUFFER_EMPTY;

    Msg.ulVersion= SECBUFFER_VERSION;    // Version number
    Msg.cBuffers= 4;
    Msg.pBuffers= Buffers;

    sRet = DecryptMessage(phContext, &Msg, 0, NULL);

    /* Check for possible errors: we continue in case context has 
       expired or renogitiation is required */
    if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED &&
        sRet != SEC_I_RENEGOTIATE && sRet != SEC_E_INCOMPLETE_MESSAGE)
    {
      ma_schannel_set_sec_error(pvio, sRet);
      return sRet;
    }

    pData= pExtra= NULL;
    for (i=0; i < 4; i++)
    {
      if (!pData && Buffers[i].BufferType == SECBUFFER_DATA)
        pData= &Buffers[i];
      if (!pExtra && Buffers[i].BufferType == SECBUFFER_EXTRA)
        pExtra= &Buffers[i];
      if (pData && pExtra)
        break;
    }
    
    if (pData && pData->cbBuffer)
    {
      memcpy(ReadBuffer + *DecryptLength, pData->pvBuffer, pData->cbBuffer);
      *DecryptLength+= pData->cbBuffer;
      return sRet;
    }

    if (pExtra)
    {
      MoveMemory(sctx->IoBuffer, pExtra->pvBuffer, pExtra->cbBuffer);
      dwOffset= pExtra->cbBuffer;
    }
    else
      dwOffset= 0;
  }    
}
SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls)
{
  MARIADB_PVIO *pvio;
  SECURITY_STATUS sRet;
  DWORD OutFlags;
  DWORD r;
  SC_CTX *sctx;
  SecBuffer ExtraData;
  DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
                ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | 
                ISC_REQ_USE_SUPPLIED_CREDS |
                ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
  
  SecBufferDesc	BufferOut;
  SecBuffer  BuffersOut;

  if (!ctls || !ctls->pvio)
    return 1;

  pvio= ctls->pvio;
  sctx= (SC_CTX *)ctls->ssl;

  /* Initialie securifty context */
  BuffersOut.BufferType= SECBUFFER_TOKEN;
  BuffersOut.cbBuffer= 0;
  BuffersOut.pvBuffer= NULL;


  BufferOut.cBuffers= 1;
  BufferOut.pBuffers= &BuffersOut;
  BufferOut.ulVersion= SECBUFFER_VERSION;

  sRet = InitializeSecurityContext(&sctx->CredHdl,
                                    NULL,
                                    pvio->mysql->host,
                                    SFlags,
                                    0,
                                    SECURITY_NATIVE_DREP,
                                    NULL,
                                    0,
                                    &sctx->ctxt,
                                    &BufferOut,
                                    &OutFlags,
                                    NULL);

  if(sRet != SEC_I_CONTINUE_NEEDED)
  {
    ma_schannel_set_sec_error(pvio, sRet);
    return sRet;
  }

  /* send client hello packaet */
  if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
  {  
    r= (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
    if (r <= 0)
    {
      sRet= SEC_E_INTERNAL_ERROR;
      goto end;
    }
  }
  sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);

  /* allocate IO-Buffer for write operations: After handshake
  was successfull, we are able now to calculate payload */
  if ((sRet = QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
    goto end;

  sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
  if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
  {
    sRet= SEC_E_INSUFFICIENT_MEMORY;
    goto end;
  }
    
  return sRet;
end:
  LocalFree(sctx->IoBuffer);
  sctx->IoBufferSize= 0;
  FreeContextBuffer(BuffersOut.pvBuffer);
  DeleteSecurityContext(&sctx->ctxt);
  return sRet;
}
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
{
  SecBufferDesc   OutBuffer, InBuffer;
  SecBuffer       InBuffers[2], OutBuffers;
  DWORD           dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
  TimeStamp       tsExpiry;
  SECURITY_STATUS rc;
  PUCHAR          IoBuffer;
  BOOL            fDoRead;
  MARIADB_TLS     *ctls= pvio->ctls;
  SC_CTX          *sctx= (SC_CTX *)ctls->ssl;


  dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
                ISC_REQ_REPLAY_DETECT |
                ISC_REQ_CONFIDENTIALITY |
                ISC_RET_EXTENDED_ERROR |
                ISC_REQ_ALLOCATE_MEMORY | 
                ISC_REQ_STREAM;


  /* Allocate data buffer */
  if (!(IoBuffer = LocalAlloc(LMEM_FIXED, SC_IO_BUFFER_SIZE)))
    return SEC_E_INSUFFICIENT_MEMORY;

  cbIoBuffer = 0;
  fDoRead = InitialRead;

  /* handshake loop: We will leave a handshake is finished
     or an error occurs */

  rc = SEC_I_CONTINUE_NEEDED;

  while (rc == SEC_I_CONTINUE_NEEDED ||
         rc == SEC_E_INCOMPLETE_MESSAGE ||
         rc == SEC_I_INCOMPLETE_CREDENTIALS )
  {
    /* Read data */
    if (rc == SEC_E_INCOMPLETE_MESSAGE ||
        !cbIoBuffer)
    {
      if(fDoRead)
      {
        cbData = (DWORD)pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
        if (cbData == SOCKET_ERROR || cbData == 0)
        {
          rc = SEC_E_INTERNAL_ERROR;
          break;
        }
        cbIoBuffer += cbData;
      }
      else
        fDoRead = TRUE;
    }

    /* input buffers
       First buffer stores data received from server. leftover data
       will be stored in second buffer with BufferType SECBUFFER_EXTRA */

    InBuffers[0].pvBuffer   = IoBuffer;
    InBuffers[0].cbBuffer   = cbIoBuffer;
    InBuffers[0].BufferType = SECBUFFER_TOKEN;

    InBuffers[1].pvBuffer   = NULL;
    InBuffers[1].cbBuffer   = 0;
    InBuffers[1].BufferType = SECBUFFER_EMPTY;

    InBuffer.cBuffers       = 2;
    InBuffer.pBuffers       = InBuffers;
    InBuffer.ulVersion      = SECBUFFER_VERSION;


    /* output buffer */
    OutBuffers.pvBuffer  = NULL;
    OutBuffers.BufferType= SECBUFFER_TOKEN;
    OutBuffers.cbBuffer  = 0;

    OutBuffer.cBuffers      = 1;
    OutBuffer.pBuffers      = &OutBuffers;
    OutBuffer.ulVersion     = SECBUFFER_VERSION;


    rc = InitializeSecurityContextA(&sctx->CredHdl,
                                    &sctx->ctxt,
                                    NULL,
                                    dwSSPIFlags,
                                    0,
                                    SECURITY_NATIVE_DREP,
                                    &InBuffer,
                                    0,
                                    NULL,
                                    &OutBuffer,
                                    &dwSSPIOutFlags,
                                    &tsExpiry );


    if (rc == SEC_E_OK  ||
        rc == SEC_I_CONTINUE_NEEDED ||
        FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
    {
      if(OutBuffers.cbBuffer && OutBuffers.pvBuffer)
      {
        cbData= (DWORD)pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
          FreeContextBuffer(OutBuffers.pvBuffer);
          DeleteSecurityContext(&sctx->ctxt);
          return SEC_E_INTERNAL_ERROR;
        }

        /* Free output context buffer */
        FreeContextBuffer(OutBuffers.pvBuffer);
        OutBuffers.pvBuffer = NULL;
      }
    }

    /* check if we need to read more data */
    switch (rc) {
    case SEC_E_INCOMPLETE_MESSAGE:
      /* we didn't receive all data, so just continue loop */
      continue;
      break;
    case SEC_E_OK:
      /* handshake completed, but we need to check if extra
         data was sent (which contains encrypted application data) */
      if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
      {
        if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
          return SEC_E_INSUFFICIENT_MEMORY;
        
        MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
        pExtraData->BufferType = SECBUFFER_TOKEN;
        pExtraData->cbBuffer   = InBuffers[1].cbBuffer;
      }
      else
      {
        pExtraData->BufferType= SECBUFFER_EMPTY;
        pExtraData->pvBuffer= NULL;
        pExtraData->cbBuffer= 0;
      }
    break;
 
    case SEC_I_INCOMPLETE_CREDENTIALS:
      /* Provided credentials didn't contain a valid client certificate.
         We will try to connect anonymously, using current credentials */
      fDoRead= FALSE;
      rc= SEC_I_CONTINUE_NEEDED;
      continue;
      break;
    default:
      if (FAILED(rc))
      {
        ma_schannel_set_sec_error(pvio, rc);
        goto loopend;
      }
      break;
    }

    if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
    {
      MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
      cbIoBuffer = InBuffers[1].cbBuffer;
    }

    cbIoBuffer = 0;
  }
loopend:
  if (FAILED(rc)) 
    DeleteSecurityContext(&sctx->ctxt);
  LocalFree(IoBuffer);

  return rc;
}
示例#5
0
int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
{
  SC_CTX *sctx= (SC_CTX *)ctls->ssl;
  MARIADB_PVIO *pvio= ctls->pvio;
  int rc= 1;
  char *szName= NULL;
  char *pszServerName= pvio->mysql->host;

  /* check server name */
  if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
  {
    PCCERT_CONTEXT pServerCert;
    DWORD NameSize= 0;
    char *p1, *p2;
    SECURITY_STATUS sRet;

    if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
    {
      ma_schannel_set_sec_error(pvio, sRet);
      return 1;
    }

    if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType,
      &pServerCert->pCertInfo->Subject,
      CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
      NULL, 0)))
    {
      pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
      return 1;
    }

    if (!(szName= (char *)LocalAlloc(0, NameSize + 1)))
    {
      pvio->set_error(sctx->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
      goto end;
    }

    if (!CertNameToStr(pServerCert->dwCertEncodingType,
      &pServerCert->pCertInfo->Subject,
      CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
      szName, NameSize))
    {
      pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
      goto end;
    }
    if ((p1 = strstr(szName, "CN=")))
    {
      p1+= 3;
      if ((p2= strstr(p1, ", ")))
        *p2= 0;
      if (!strcmp(pszServerName, p1))
      {
        rc= 0;
        goto end;
      }
      pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
                     "Name of server certificate didn't match");
    }
  }
end:
  if (szName)
    LocalFree(szName);
  return rc;
}
示例#6
0
my_bool ma_tls_connect(MARIADB_TLS *ctls)
{
  my_bool blocking;
  MYSQL *mysql;
  SCHANNEL_CRED Cred;
  MARIADB_PVIO *pvio;
  my_bool rc= 1;
  SC_CTX *sctx;
  SECURITY_STATUS sRet;
  PCCERT_CONTEXT pRemoteCertContext = NULL,
                 pLocalCertContext= NULL;
  ALG_ID AlgId[MAX_ALG_ID]= {0};
  
  if (!ctls || !ctls->pvio)
    return 1;;
  
  pvio= ctls->pvio;
  sctx= (SC_CTX *)ctls->ssl;

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

  mysql= pvio->mysql;
 
  if (ma_tls_set_client_certs(ctls))
    goto end;

  ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));

  /* Set cipher */
  if (mysql->options.ssl_cipher)
  {
    WORD validTokens = 0;
    char *token = strtok(mysql->options.ssl_cipher, ":");
    while (token)
    {
      struct st_cipher_suite *valid;
      for (valid = valid_ciphers; valid && valid->aid; valid++)
      {
        if (!strcmp(token, valid->cipher))
        {
          AlgId[validTokens++] = valid->aid;
          break;
        }
      }
      token = strtok(NULL, ":");
    }
  }
  Cred.palgSupportedAlgs = (ALG_ID *)&AlgId;
  
  Cred.dwVersion= SCHANNEL_CRED_VERSION;
  if (mysql->options.extension)
    Cred.dwMinimumCipherStrength = MAX(128, mysql->options.extension->tls_cipher_strength);
  else
    Cred.dwMinimumCipherStrength = 128;
  Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT |
    SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
  if (sctx->client_cert_ctx)
  {
    Cred.cCreds = 1;
    Cred.paCred = &sctx->client_cert_ctx;
  }
  if (mysql->options.extension && mysql->options.extension->tls_version)
  {
    Cred.grbitEnabledProtocols= 0;
    if (strstr("TLSv1.0", mysql->options.extension->tls_version))
      Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0;
    if (strstr("TLSv1.1", mysql->options.extension->tls_version))
      Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1;
    if (strstr("TLSv1.2", mysql->options.extension->tls_version))
      Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2;
  }
  else
    Cred.grbitEnabledProtocols = SP_PROT_TLS1_0 |
                                 SP_PROT_TLS1_1 |
                                 SP_PROT_TLS1_2;

  if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
                                       NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK)
  {
    ma_schannel_set_sec_error(pvio, sRet);
    goto end;
  }
  sctx->FreeCredHdl= 1;

  if (ma_schannel_client_handshake(ctls) != SEC_E_OK)
    goto end;

  sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
  if (sRet != SEC_E_OK)
  {
    ma_schannel_set_sec_error(pvio, sRet);
    goto end;
  }
  
  if (!ma_schannel_verify_certs(sctx, 0))
    goto end;
 
  return 0;

end:
  if (pRemoteCertContext)
    CertFreeCertificateContext(pRemoteCertContext);
  if (rc && sctx->IoBufferSize)
    LocalFree(sctx->IoBuffer);
  sctx->IoBufferSize= 0;
  if (sctx->client_cert_ctx)
    CertFreeCertificateContext(sctx->client_cert_ctx);
  sctx->client_cert_ctx= 0;
  return 1;
}