All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.StreamColumns Maven / Gradle / Ivy

//---------------------------------------------------------------------------------------------------------------------------------
// File: StreamColumns.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), 
//  to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
//  IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
 

package com.microsoft.sqlserver.jdbc;

/**
* StreamColumns stores the column meta data for a result set. StreamColumns parses the inbound
* TDS packet stream to determine column meta data.
*/

final class StreamColumns extends StreamPacket
{
  /** the set of columns */
  private Column[] columns;

  /* The CekTable. */
  private CekTable cekTable = null;

  private boolean shouldHonorAEForRead = false;

  /* Gets the CekTable */
  CekTable getCekTable()
  {
	  return cekTable;
  }
  
  /**
   * Create a new stream columns
   */
  /*L0*/ StreamColumns() {
    super(TDS.TDS_COLMETADATA);
  }

  /**
   * Create a new stream columns with column encryption setting
   */
  StreamColumns(boolean honorAE) {
    super(TDS.TDS_COLMETADATA);
    shouldHonorAEForRead = honorAE;
  }

  /**
   * Parse a result set column meta data TDS stream for CEK table entry.
   *
   * @throws SQLServerException
   */
  CekTableEntry readCEKTableEntry(TDSReader tdsReader) throws SQLServerException
  {
      // Read the DB ID
      int databaseId = tdsReader.readInt();

      // Read the keyID
      int cekId = tdsReader.readInt();

      // Read the key version
      int cekVersion = tdsReader.readInt();

      // Read the key MD Version
      byte[] cekMdVersion = new byte[8];
      tdsReader.readBytes(cekMdVersion, 0, 8); 

      // Read the value count
      int cekValueCount = tdsReader.readUnsignedByte();

	  CekTableEntry cekTableEntry = new CekTableEntry(cekValueCount);

      for (int i = 0; i < cekValueCount; i++) {
          // Read individual CEK values                    
          short encryptedCEKlength = tdsReader.readShort();
          
          byte[] encryptedCek = new byte[encryptedCEKlength];

          // Read the actual encrypted CEK
          tdsReader.readBytes(encryptedCek, 0, encryptedCEKlength);

          // Read the length of key store name
          int keyStoreLength = tdsReader.readUnsignedByte();           
          
          // And read the key store name now
          String keyStoreName = tdsReader.readUnicodeString(keyStoreLength);
          
          // Read the length of key Path
          int keyPathLength = tdsReader.readShort();
          
          // Read the key path string
          String keyPath = tdsReader.readUnicodeString(keyPathLength);
          
          // Read the length of the string carrying the encryption algo
          int algorithmLength = tdsReader.readUnsignedByte();

          // Read the string carrying the encryption algo  (eg. RSA_PKCS_OAEP)
          String algorithmName = tdsReader.readUnicodeString(algorithmLength);

          // Add this encrypted CEK blob to our list of encrypted values for the CEK
          cekTableEntry.add(
        		  encryptedCek,
        		  databaseId,
        		  cekId,
        		  cekVersion,
        		  cekMdVersion,
        		  keyPath,
        		  keyStoreName,
        		  algorithmName);
      }
      return cekTableEntry;
  }
  
  /**
   * Parse a result set column meta data TDS stream for CEK table.
   *
   * @throws SQLServerException
   */
  void readCEKTable(TDSReader tdsReader) throws SQLServerException
  {
      // Read count
      int tableSize = tdsReader.readShort();
      
      // CEK table will be sent if AE is enabled. If none of the columns are encrypted, the CEK table
      // size would be zero.
      if (0 != tableSize) {
    	  cekTable = new CekTable(tableSize);
    	  
          // Read individual entries
          for (int i = 0; i < tableSize; i++) {
              //SqlTceCipherInfoEntry entry;
              cekTable.setCekTableEntry(i, readCEKTableEntry(tdsReader));
          }
      }
  }

  
  /**
   * Parse a result set column meta data TDS stream for crypto metadata for AE.
   *
   * @throws SQLServerException
   */
  CryptoMetadata readCryptoMetadata(TDSReader tdsReader) throws SQLServerException
  {	  
	  short ordinal = 0;
      // Read the ordinal into cipher table. For return values there is not cipher table and no ordinal.
	  if (null != cekTable)
	  {
		  ordinal = tdsReader.readShort();
	  }

      TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, false);
	  	  
      // Read the cipher algorithm Id
      byte algorithmId = (byte) tdsReader.readUnsignedByte();

      String algorithmName = null;
      if ((byte) TDS.CUSTOM_CIPHER_ALGORITHM_ID == algorithmId) {
          // Custom encryption algorithm, read the name
          int nameSize = tdsReader.readUnsignedByte();
          algorithmName = tdsReader.readUnicodeString(nameSize);
      }

      // Read Encryption Type. 
      byte encryptionType = (byte) tdsReader.readUnsignedByte();

      // Read Normalization Rule Version.
      byte normalizationRuleVersion = (byte) tdsReader.readUnsignedByte();
      
      CryptoMetadata cryptoMeta = new CryptoMetadata(
            (cekTable == null) ? null : cekTable.getCekTableEntry(ordinal), 
    		  ordinal, 
    		  algorithmId, 
    		  algorithmName, 
    		  encryptionType, 
    		  normalizationRuleVersion);
      cryptoMeta.setBaseTypeInfo(typeInfo);
      
      return cryptoMeta;
  }
  
  /**
   * Parse a result set column meta data TDS stream.
   *
   * @throws SQLServerException
   */
  void setFromTDS(TDSReader tdsReader) throws SQLServerException
  {
    if (TDS.TDS_COLMETADATA != tdsReader.readUnsignedByte()) assert false;

    int nTotColumns = tdsReader.readUnsignedShort();

    // Handle the magic NoMetaData value
    if (0xFFFF == nTotColumns)
      return;

    if (tdsReader.getServerSupportsColumnEncryption())
    {
    	readCEKTable(tdsReader);
    }    
    
    this.columns = new Column[nTotColumns];

    for (int numColumns = 0; numColumns < nTotColumns; numColumns++)
    {
      // Column type info
      TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, true);

      // Table name
      // 
      // Table name is set at this point for TEXT/NTEXT/IMAGE columns only.
      // For other columns, table name may be set via COLUMNINFO and TABNAME tokens.
      SQLIdentifier tableName = new SQLIdentifier();
      if (SSType.TEXT == typeInfo.getSSType() ||
          SSType.NTEXT == typeInfo.getSSType() ||
          SSType.IMAGE == typeInfo.getSSType())
      {
          // Yukon and later, table names are returned as multi-part SQL identifiers.
          tableName = tdsReader.readSQLIdentifier();        
      }

      CryptoMetadata cryptoMeta = null;
      if (tdsReader.getServerSupportsColumnEncryption() && typeInfo.isEncrypted())
      {
    	  cryptoMeta = readCryptoMetadata(tdsReader);
    	  cryptoMeta.baseTypeInfo.setFlags(typeInfo.getFlagsAsShort());
    	  typeInfo.setSQLCollation(cryptoMeta.baseTypeInfo.getSQLCollation());
      }
      
      // Column name
      String columnName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());

      if (shouldHonorAEForRead)
      {
      this.columns[numColumns] = new Column(typeInfo, columnName, tableName, cryptoMeta);
    }
      else
      {
    	  // Set null for crypto metadata if column encryption setting is off at the connection or at the statement level.
    	  this.columns[numColumns] = new Column(typeInfo, columnName, tableName, null);
      }
    }
  }

  /**
   * Applies per-column table information derived from COLINFO and TABNAME tokens to the
   * set of columns defined by this COLMETADATA token to produce the complete set of
   * column information.
   */
  Column[] buildColumns(StreamColInfo colInfoToken, StreamTabName tabNameToken) throws SQLServerException
  {
    if (null != colInfoToken && null != tabNameToken)
      tabNameToken.applyTo(columns, colInfoToken.applyTo(columns));

    return columns;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy