/* Header file to send and recieve SPIN/SPOUT commands to SCSI device Original program copyright 2010 John D. Coleman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #if defined(OS_LINUX) #include #include #define SCSI_TIMEOUT 5000 #elif defined(OS_FREEBSD) #include #define SCSI_TIMEOUT 5000 #elif defined(OS_AIX) #define _LINUX_SOURCE_COMPAT #include #include #include #include #define SCSI_TIMEOUT 5 #else #error "OS type is not set" #endif #include #include "scsiencrypt.h" #define SSP_SPIN_OPCODE 0XA2 #define SSP_SPOUT_OPCODE 0XB5 #define SSP_SP_CMD_LEN 12 #define SSP_SP_PROTOCOL_TDE 0X20 #define RETRYCOUNT 1 #define BSINTTOCHAR(x) (unsigned char)((x & 0xff000000)>>24), (unsigned char)((x & 0x00ff0000)>>16),(unsigned char)((x & 0x0000ff00)>>8),(unsigned char)(x & 0x000000ff) using namespace std; void byteswap(unsigned char* array,int size,int value); bool moveTape(std::string tapeDevice,int count,bool dirForward); void outputSense(SCSI_PAGE_SENSE* sd); void readIOError(int err); bool SCSIExecute(string tapedevice, unsigned char* cmd_p,int cmd_len,unsigned char* dxfer_p,int dxfer_len, bool cmd_to_device, bool show_error); typedef struct { //structure for setting data encryption unsigned char pageCode [2]; unsigned char length [2]; #if STENC_BIG_ENDIAN == 1 unsigned char scope :3; unsigned char res_bits_1 :4; unsigned char lock :1; #else unsigned char lock :1; unsigned char res_bits_1 :4; unsigned char scope :3; #endif #if STENC_BIG_ENDIAN == 1 unsigned char CEEM :2; unsigned char RDMC :2; unsigned char sdk :1; unsigned char ckod :1; unsigned char ckorp :1; unsigned char ckorl :1; #else unsigned char ckorl :1; unsigned char ckorp :1; unsigned char ckod :1; unsigned char sdk :1; unsigned char RDMC :2; unsigned char CEEM :2; #endif unsigned char encryptionMode; unsigned char decryptionMode; unsigned char algorithmIndex; unsigned char keyFormat; unsigned char res_bits_2 [8]; unsigned char keyLength [2]; unsigned char keyData [SSP_KEY_LENGTH]; } SSP_PAGE_SDE; unsigned char scsi_sense_command[6]={ 0x03, 0,0,0, sizeof(SCSI_PAGE_SENSE), 0 }, scsi_inq_command[6] = { 0x12, 0,0,0, sizeof(SCSI_PAGE_INQ), 0 }, spin_des_command [SSP_SP_CMD_LEN] = { SSP_SPIN_OPCODE, SSP_SP_PROTOCOL_TDE, 0, 0X20, 0,0, BSINTTOCHAR(sizeof(SSP_PAGE_BUFFER)), 0,0 }, spin_nbes_command [SSP_SP_CMD_LEN] = { SSP_SPIN_OPCODE, SSP_SP_PROTOCOL_TDE, 0, 0X21, 0,0, BSINTTOCHAR(sizeof(SSP_PAGE_BUFFER)), 0,0 }; //Gets encryption options on the tape drive SSP_DES* SSPGetDES(string tapeDevice){ SSP_PAGE_BUFFER buffer; memset(&buffer,0,sizeof(SSP_PAGE_BUFFER)); if(!SCSIExecute(tapeDevice, (unsigned char*)&spin_des_command, sizeof(spin_des_command), (unsigned char*)&buffer, sizeof(SSP_PAGE_BUFFER), false,true)) { return NULL; } SSP_DES* status=new SSP_DES(&buffer); return status; } //Gets encryption options on the tape drive SSP_NBES* SSPGetNBES(string tapeDevice,bool retry){ SSP_PAGE_BUFFER buffer; memset(&buffer,0,sizeof(SSP_PAGE_BUFFER)); if(!SCSIExecute(tapeDevice, (unsigned char*)&spin_nbes_command, sizeof(spin_nbes_command), (unsigned char*)&buffer, sizeof(SSP_PAGE_BUFFER), false,false)) { return NULL; } SSP_NBES* status=new SSP_NBES(&buffer); if(status->nbes.encryptionStatus==0x01 && retry){ //move to the start of the tape and try again int moves=0; while(true){ if(status==NULL)break; if(status->nbes.encryptionStatus!=0x01)break; if(moves>=MAX_TAPE_READ_BLOCKS)break; delete status; status=NULL; //double free bug fix provided by Adam Nielsen if(!moveTape(tapeDevice,1,true))break; moves++; status=SSPGetNBES(tapeDevice,false); } moveTape(tapeDevice,moves,false); } return status; } //Sends and inquiry to the device SCSI_PAGE_INQ* SCSIGetInquiry(string tapeDevice){ SCSI_PAGE_INQ* status=new SCSI_PAGE_INQ; memset(status,0,sizeof(SCSI_PAGE_INQ)); if(!SCSIExecute(tapeDevice, (unsigned char*)&scsi_inq_command, sizeof(scsi_inq_command), (unsigned char*)status, sizeof(SCSI_PAGE_INQ), false,true)) { exit(EXIT_FAILURE); } return status; } //Writes encryption options to the tape drive bool SCSIWriteEncryptOptions(string tapeDevice, SCSIEncryptOptions* eOptions){ char buffer[1024]; memset(&buffer,0,1024); SSP_PAGE_SDE options; //copy the template over the options memset(&options,0, sizeof(SSP_PAGE_SDE)); byteswap((unsigned char*)&options.pageCode,2,0x10); int pagelen=sizeof(SSP_PAGE_SDE); options.scope=2; //all IT nexus = 10b options.RDMC=eOptions->rdmc; options.ckod=(eOptions->CKOD)?1:0; options.CEEM=DEFAULT_CEEM; options.algorithmIndex=eOptions->algorithmIndex; //set the specific options switch(eOptions->cryptMode){ case CRYPTMODE_ON: //encrypt, read only encrypted data options.encryptionMode=2; options.decryptionMode=2; break; case CRYPTMODE_MIXED: //encrypt, read all data options.encryptionMode=2; options.decryptionMode=3; break; case CRYPTMODE_RAWREAD: options.encryptionMode=2; options.decryptionMode=1; break; default: byteswap((unsigned char*)options.keyLength,2,DEFAULT_KEYSIZE); eOptions->cryptoKey=""; //blank the key eOptions->keyName=""; //blank the key name, not supported when turned off break; } if(eOptions->cryptoKey!=""){ //byte swap the keylength byteswap((unsigned char*)&options.keyLength,2,eOptions->cryptoKey.size()); //copy the crypto key into the options eOptions->cryptoKey.copy((char*)&options.keyData, eOptions->cryptoKey.size(),0); } //create the key descriptor if(eOptions->keyName!=""){ SSP_KAD kad; memset(&kad,0,sizeof(kad)); kad.type=0x00; kad.authenticated=0; //set the descriptor length to the length of the keyName byteswap((unsigned char*)&kad.descriptorLength,2,eOptions->keyName.size()); //get the size of the kad object int kadlen=eOptions->keyName.size()+SSP_KAD_HEAD_LENGTH; //increment the SPOUT page len pagelen+=kadlen; //increase the page size eOptions->keyName.copy((char*)&kad.descriptor,eOptions->keyName.size(),0); //copy the kad after the SDE command memcpy(&buffer[sizeof(SSP_PAGE_SDE)],&kad,kadlen); } //update the pagelen in options byteswap((unsigned char*)&options.length,2,pagelen-4); //set the page length, minus the length and pageCode //copy the options to the beginning of the buffer memcpy(&buffer,&options,sizeof(SSP_PAGE_SDE)); unsigned char spout_sde_command [SSP_SP_CMD_LEN] = { SSP_SPOUT_OPCODE, SSP_SP_PROTOCOL_TDE, 0, 0X10, 0,0, BSINTTOCHAR(pagelen), 0,0 }; //return whether or not the command executed return SCSIExecute( tapeDevice, (unsigned char*)&spout_sde_command, sizeof(spout_sde_command), (unsigned char*)&buffer, pagelen, true,true ); } bool SCSIExecute(string tapedrive, unsigned char* cmd_p,int cmd_len,unsigned char* dxfer_p,int dxfer_len, bool cmd_to_device, bool show_error) { const char* tapedevice=tapedrive.c_str(); int sg_fd,eresult,sresult,ioerr,retries; SCSI_PAGE_SENSE* sd=new SCSI_PAGE_SENSE; memset(sd,0,sizeof(SCSI_PAGE_SENSE)); #if defined(OS_LINUX) || defined(OS_FREEBSD) // Linux or FreeBSD System errno=0; sg_fd = open(tapedevice, O_RDONLY); if( sg_fd==-1){ cerr<<"Could not open device '"<>8); array[1]=(unsigned char)(value & 0x00ff); break; case 4: array[0]=(unsigned char)((value & 0xff000000)>>24); array[1]=(unsigned char)((value & 0x00ff0000)>>16); array[2]=(unsigned char)((value & 0x0000ff00)>>8); array[3]=(unsigned char)(value & 0x000000ff); break; default: cout<<"Unhandled byte swap length of "<length)+4; int pos=start; while(pos=length)break; unsigned short kadDesLen=BSSHORT(kad.descriptorLength); if(kadDesLen>0){ memcpy(&kad.descriptor,rawbuff+pos,kadDesLen); pos+=kadDesLen; }else pos++; kads.push_back(kad); } } bool moveTape(std::string tapeDevice,int count,bool dirForward){ struct mtop mt_command; int sg_fd = open(tapeDevice.c_str(), O_RDONLY); if(!sg_fd || sg_fd==-1){ return false; } errno=0; bool retval=true; #if defined(OS_LINUX) || defined(OS_FREEBSD) // Linux or FreeBSD System mt_command.mt_op = (dirForward)?MTFSR:MTBSR; mt_command.mt_count = count; ioctl(sg_fd, MTIOCTOP, &mt_command); #elif defined(OS_AIX) mt_command.st_op = (dirForward)?MTFSR:MTBSR; mt_command.st_count = count; ioctl(sg_fd, STIOCTOP, &mt_command); #else #error "OS type is not set" #endif if(errno!=0)retval=false; close(sg_fd); errno=0; return retval; } void readIOError(int err){ if(err==0)return; cerr<<"ERROR: "; switch(err){ case EAGAIN: cerr<<"Device already open"<senseKey){ case 0: cerr<<"No specific error"; break; case 2: cerr<<"Device not ready"; break; case 3: cerr<<"Medium Error"; break; case 4: cerr<<"Hardware Error"; break; case 5: cerr<<"Illegal Request"; break; case 6: cerr<<"Unit Attention"; break; case 7: cerr<<"Data protect"; break; case 8: cerr<<"Blank tape"; break; } cerr<<" (0x"<senseKey)<<")"<addSenseCode)<addSenseCodeQual)<addSenseLen>0){ cerr<addSenseLen;i++){ cerr<addSenseData[i]); } cerr<