我正在尝试利用我的fuse文件系统中使用 ioctl 的LTO5设备的硬件加密。它应该很简单,用cdb设置一个io_hdr:
0xB5, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00
并根据所选算法提供关键页面。当我执行SG_IO ioctl 时,我返回0,带有所有0的SCSI感。
现在,这里变得奇怪了。写入设备的数据从不加密。我将磁带切换到另一个驱动器并进行SCSI读取,数据全部为明文。
因此,下一步是执行SCSI SPIN命令以查看SPOUT命令是否已执行,即使所有返回都表明它已执行。在SPOUT之后,我立即发送一个SPIN作为安全状态页面(0x20):
0xA2, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00
同样,从SG_IO ioctl 返回值0并感觉全零。然而,我得到的页面是我放入驱动器的SPOUT键页面。我确信这不是驱动器出来的,因为它有我发送的密钥,这绝对是SCSI规范否。顺便说一下,我为两个SCSI命令使用一个完全不同的缓冲区,并将SPIN页面设置为零,只是为了测量。 sg驱动程序IS提供此数据以响应SPIN。
任何人都可以对这种行为有所了解吗?
Linux archive.xxxxx.xxx 2.6.18-274.7.1.el5#1 SMP Thu Oct 20 16:21:01 EDT 2011 x86_64 x86_64 x86_64 GNU / Linux
sg3_utils-库-1.25-5.el5
SGPIO-1.2.0_10-2.el5
MT-ST-0.9b-2.2.2
我将 ioctl 命令发送到/ dev / sg5:
[root@archive bin]# sg_inq /dev/sg5
standard INQUIRY:
PQual=0 Device_type=1 RMB=1 version=0x06 [SPC-4]
[AERC=0] [TrmTsk=0] NormACA=0 HiSUP=0 Resp_data_format=2
SCCS=0 ACC=0 TPGS=0 3PC=0 Protect=1 BQue=0
EncServ=0 MultiP=1 (VS=0) [MChngr=0] [ACKREQQ=0] Addr16=0
[RelAdr=0] WBus16=0 Sync=0 Linked=0 [TranDis=0] CmdQue=1
[SPI: Clocking=0x0 QAS=0 IUS=0]
length=70 (0x46) Peripheral device type: tape
Vendor identification: IBM
Product identification: ULTRIUM-HH5
Product revision level: BAKG
Unit serial number: 106xxxxxxxxxx
我发现Linux ioctl系统调用返回SG_ERR_DID_ERROR [0x07]在sg_io_hdr_t的host_status成员中的主机适配器中检测到内部错误。
setltokey.c代码也在这里:http://www.circlesoft.com/setltokey.c
/*
setLTO4key: Set LTO4 Encryption Key
Copyright (c) 2008 Andrew Schretter <schrett@math.duke.edu>
Provided under GPL license
added clear encryption,
sense key and
error printouts - Gerard J. Cerchio <gjpc@circlesoft.com>
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <scsi/sg.h>
#include <scsi/scsi.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define SENSE_BUFF_LEN 96 /* from lto spec */
/*
* here is a sample key - create a file with these HEX digits:
4418AFCD046F2535B2E996123CE7DE3D418A15915A091C4BA12BDC85D4069A77
*/
/*
* A good sg_io_hdr_t reference: http://tldp.org/HOWTO/SCSI-Generic-HOWTO/sg_io_hdr_t.html
*/
/* Print a hexadecimal dump of a block of data */
void hexdump(void *data, int size)
{
unsigned char *p = data;
unsigned char c;
int n;
char bytestr[4] = {0};
char addrstr[10] = {0};
char hexstr[ 16*3 + 5] = {0};
char charstr[16*1 + 5] = {0};
for(n=1;n<=size;n++) {
if (n%16 == 1) {
/* store address for this line */
snprintf(addrstr, sizeof(addrstr), "%.4x",
((unsigned int)p-(unsigned int)data) );
}
c = *p;
if (isalnum(c) == 0) {
c = '.';
}
/* store hex str (for left side) */
snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
/* store char str (for right side) */
snprintf(bytestr, sizeof(bytestr), "%c", c);
strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
if(n%16 == 0) {
/* line completed */
printf(" [%4.4s] %-49.49s %s\n", addrstr, hexstr, charstr);
hexstr[0] = 0;
charstr[0] = 0;
} else if(n%8 == 0) {
/* half line: add whitespaces */
strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1);
}
p++; /* next byte */
}
if (strlen(hexstr) > 0) {
/* print rest of buffer if not empty */
printf(" [%4.4s] %-49.49s %s\n", addrstr, hexstr, charstr);
}
}
/* Send a SCSI command block and display the result. */
void do_read_command(int fd, char *desc, unsigned char *cmd, int len)
{
unsigned char sense[SENSE_BUFF_LEN];
memset( sense, 0, SENSE_BUFF_LEN );
sg_io_hdr_t io;
unsigned char buf[512];
memset(buf, 0, sizeof(buf));
memset(&io, 0, sizeof(io));
io.interface_id = 'S';
io.cmd_len = len;
io.mx_sb_len = 0;
io.dxfer_direction = SG_DXFER_FROM_DEV;
io.dxfer_len = sizeof(buf);
io.dxferp = buf;
io.cmdp = cmd;
printf("Command: %s\n", desc);
hexdump(cmd, len);
if (ioctl(fd, SG_IO, &io) < 0) {
printf("Error: %s\n", strerror(errno));
return;
}
if ( io.sb_len_wr ){
printf("Sense\n");
hexdump( sense, SENSE_BUFF_LEN );
}
else
printf( "No Sense\n" );
if ((io.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
printf("Failed with info 0x%02x mask status 0x%02x msg status 0x%02x host status 0x%02x driver status 0x%02x\n", io.info, io.masked_status, io.msg_status, io.host_status, io.driver_status );
return;
}
len = io.dxfer_len - io.resid;
printf("Response: %d %s\n", len, (len == 1) ? "byte" : "bytes");
hexdump(buf, len);
}
void do_write_command(int fd, char *desc, unsigned char *cmd, int len, char *data_desc, unsigned char *data, int datalen)
{
unsigned char sense[SENSE_BUFF_LEN];
memset( sense, 0, SENSE_BUFF_LEN );
sg_io_hdr_t io;
memset(&io, 0, sizeof(io));
io.interface_id = 'S';
io.cmd_len = len;
io.mx_sb_len = SENSE_BUFF_LEN;
io.dxfer_direction = SG_DXFER_TO_DEV;
io.dxfer_len = datalen;
io.dxferp = data;
io.cmdp = cmd;
io.sbp = sense;
printf("Command: %s\n", desc);
hexdump(cmd, len);
printf("Data: %s\n", data_desc);
hexdump(data, datalen);
if (ioctl(fd, SG_IO, &io) < 0) {
printf("Error: %s\n", strerror(errno));
return;
}
if ( io.sb_len_wr ){
printf("Sense\n");
hexdump( sense, SENSE_BUFF_LEN );
}
else
printf( "No Sense\n" );
if ((io.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
printf("Failed with info 0x%02x mask status 0x%02x msg status 0x%02x host status 0x%02x driver status 0x%02x\n", io.info, io.masked_status, io.msg_status, io.host_status, io.driver_status );
return;
}
len = io.dxfer_len - io.resid;
printf("Response: %d %s\n", len, (len == 1) ? "byte" : "bytes");
//hexdump(buf, len);
}
struct {
char *description;
int len;
unsigned char cmd[16];
} commands[] = {
{ "SCSI Inquiry", 6,
{ 0x12, 0x00, 0x00, 0x00, 0xFF, 0x00 } },
{ "SCSI SPOUT Set Encryption Key", 12,
{ 0xb5, 0x20, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x34, 0x00, 0x00 } },
{ "SCSI SPIN Read Status", 12,
{ 0xa2, 0x20, 0x00, 0x20, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } },
{ NULL, 0, { 0 } },
};
struct {
char *description;
int len;
unsigned char cmd[64];
} data[] = {
{ "SCSI SPOUT Send Encryption Key Page", 52,
{ 0x00, 0x10, 0x00, 0x30, 0x40, 0x00,
0x02, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x20,
} },
{ "SCSI SPOUT Clear Encryption Mode Page", 52,
{ 0x00, 0x10, 0x00, 0x30, 0x40, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x20,
} },
{ NULL, 0, { 0 } },
};
int main(int argc, char **argv)
{
FILE *fd2;
int fd;
int i = 0;
if (argc < 2) {
fprintf(stderr, "usage: %s /dev/sda < <keyfile.txt> | clear >\n", *argv);
return 1;
}
if ((fd = open(argv[1], O_RDWR)) < 0) {
perror(argv[1]);
return 1;
}
if ((ioctl(fd, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000)) {
fprintf(stderr,"%s is not a sg device\n", argv[1]);
close(fd);
return 1;
}
printf("Opened %s\n", argv[1]);
/* Send query command */
do_read_command(fd, commands[0].description, commands[0].cmd, commands[0].len);
if(argc > 2) {
if ( strcasecmp( argv[2], "clear" ) == 0 ) {
do_write_command(fd, commands[1].description, commands[1].cmd, commands[1].len, data[1].description, data[1].cmd, data[1].len);
}
else
{
if ((fd2 = fopen(argv[2], "r")) < 0) {
perror(argv[2]);
return 1;
}
for (i = 0; i < 32; i++) {
if( fscanf(fd2, "%2x ", (unsigned int *) &data[0].cmd[i + 20]) != 1 ) {
fprintf(stderr, "Keyfile Error reading %s\n", argv[2]);
return 1;
}
}
fclose(fd2);
/* Set Encryption key*/
do_write_command(fd, commands[1].description, commands[1].cmd, commands[1].len, data[0].description, data[0].cmd, data[0].len);
}
}
/* Query encryption status */
do_read_command(fd, commands[2].description, commands[2].cmd, commands[2].len);
close(fd);
return 0;
}
答案 0 :(得分:1)
gjpc我正在努力完成同样的事情,你是否在这个问题上取得了进展?
更新: 我刚刚发现你的程序正在运行。 我用HP驱动器对其进行了测试,正确上传了密钥(蓝色指示灯亮)。我能够写一个磁带,上传另一个密钥,我无法读取磁带,在更改为正确的密钥后,我可以再次从磁带读取我的数据。感谢您分享您的代码。
答案 1 :(得分:0)
我发现了问题。 Dell PowerEdge R410内置的LSI 6Gb SAS SCSI HBA与IBM的ULTRIUM-HH5 LTO5磁带机不兼容。
HBA在SPIN和SPOUT命令上都失败了。我们假设它是我们使用的RedHat的版本,它比Paul的Ubuntu 10.04更旧。我们使用文件系统的内置工具执行加密,并决定在我们能够升级到更新的RedHat时重新访问该问题。
在验证过程中发现了展示塞不相容性。当我们将过期的清洁盒带装入任何驱动器时,LSI HBA正在为Qualstar RLS-8560库生成额外的SCSI MOVE MEDIA命令。此错误表示为偶尔的检测键2 ASC 3B ASQ 90返回主机的单个“MOVE MOIA MEDIA”命令。
解决方案是安装ATTO ExpressSAS H680 6Gb / s SAS / SATA HBA,PCIe 2.0,8端口外部SCSI适配器,该适配器经认证与IBM驱动器兼容。