svn commit: r251497 - in stable/9: sbin/camcontrol sys/sys
Steven Hartland
smh at FreeBSD.org
Fri Jun 7 14:10:19 UTC 2013
Author: smh
Date: Fri Jun 7 14:10:18 2013
New Revision: 251497
URL: http://svnweb.freebsd.org/changeset/base/251497
Log:
MFC r249115:
Adds security command to camcontrol which provides the ability to secure erase
SSD's
MFC r249153: (included in r251494)
mdoc: remove superfluous paragraph macro.
Modified:
stable/9/sbin/camcontrol/camcontrol.8
stable/9/sbin/camcontrol/camcontrol.c
stable/9/sys/sys/ata.h
Directory Properties:
stable/9/sbin/camcontrol/ (props changed)
stable/9/sys/ (props changed)
stable/9/sys/sys/ (props changed)
Modified: stable/9/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.8 Fri Jun 7 13:59:41 2013 (r251496)
+++ stable/9/sbin/camcontrol/camcontrol.8 Fri Jun 7 14:10:18 2013 (r251497)
@@ -228,6 +228,21 @@
.Op Fl y
.Op Fl s
.Nm
+.Ic security
+.Op device id
+.Op generic args
+.Op Fl d Ar pwd
+.Op Fl e Ar pwd
+.Op Fl f
+.Op Fl h Ar pwd
+.Op Fl k Ar pwd
+.Op Fl l Ar high|maximum
+.Op Fl q
+.Op Fl s Ar pwd
+.Op Fl T Ar timeout
+.Op Fl U Ar user|master
+.Op Fl y
+.Nm
.Ic help
.Sh DESCRIPTION
The
@@ -1072,6 +1087,123 @@ specifies automatic standby timer value
.It Ic sleep
Put ATA device into SLEEP state. Note that the only way get device out of
this state may be reset.
+.It Ic security
+Update or report security settings, using an ATA identify command (0xec).
+By default,
+.Nm
+will print out the security support and associated settings of the device.
+The
+.Ic security
+command takes several arguments:
+.Bl -tag -width 0n
+.It Fl d Ar pwd
+.Pp
+Disable device security using the given password for the selected user according
+to the devices configured security level.
+.It Fl e Ar pwd
+.Pp
+Erase the device using the given password for the selected user.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Issuing a secure erase will
+.Em ERASE ALL
+user data on the device and may take several hours to complete.
+.Pp
+When this command is used against an SSD drive all its cells will be marked as
+empty, restoring it to factory default write performance. For SSD's this action
+usually takes just a few seconds.
+.It Fl f
+.Pp
+Freeze the security configuration of the specified device.
+.Pp
+After command completion any other commands that update the device lock mode
+shall be command aborted. Frozen mode is disabled by power-off or hardware reset.
+.It Fl h Ar pwd
+.Pp
+Enhanced erase the device using the given password for the selected user.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Issuing an enhanced secure erase will
+.Em ERASE ALL
+user data on the device and may take several hours to complete.
+.Pp
+An enhanced erase writes predetermined data patterns to all user data areas,
+all previously written user data shall be overwritten, including sectors that
+are no longer in use due to reallocation.
+.It Fl k Ar pwd
+.Pp
+Unlock the device using the given password for the selected user according to
+the devices configured security level.
+.It Fl l Ar high|maximum
+.Pp
+Specifies which security level to set when issuing a
+.Fl s Ar pwd
+command. The security level determines device behavior when the master
+password is used to unlock the device. When the security level is set to high
+the device requires the unlock command and the master password to unlock.
+When the security level is set to maximum the device requires a secure erase
+with the master password to unlock.
+.Pp
+This option must be used in conjunction with one of the security action commands.
+.Pp
+Defaults to
+.Em high
+.It Fl q
+.Pp
+Be quiet, do not print any status messages.
+This option will not disable the questions, however.
+To disable questions, use the
+.Fl y
+argument, below.
+.It Fl s Ar pwd
+.Pp
+Password the device (enable security) using the given password for the selected
+user. This option can be combined with other options such as
+.Fl e Em pwd
+.Pp
+A master password may be set in a addition to the user password. The purpose of
+the master password is to allow an administrator to establish a password that
+is kept secret from the user, and which may be used to unlock the device if the
+user password is lost.
+.Pp
+.Em Note:
+Setting the master password does not enable device security.
+.Pp
+If the master password is set and the drive supports a Master Revision Code
+feature the Master Password Revision Code will be decremented.
+.It Fl T Ar timeout
+.Pp
+Overrides the default timeout, specified in seconds, used for both
+.Fl e
+and
+.Fl h
+this is useful if your system has problems processing long timeouts correctly.
+.Pp
+Usually the timeout is calculated from the information stored on the drive if
+present, otherwise it defaults to 2 hours.
+.It Fl U Ar user|master
+.Pp
+Specifies which user to set / use for the running action command, valid values
+are user or master and defaults to master if not set.
+.Pp
+This option must be used in conjunction with one of the security action commands.
+.Pp
+Defaults to
+.Em master
+.It Fl y
+.Pp
+Confirm yes to dangerous options such as
+.Fl e
+without prompting for confirmation.
+.Pp
+.El
+If the password specified for any action commands doesn't match the configured
+password for the specified user the command will fail.
+.Pp
+The password in all cases is limited to 32 characters, longer passwords will
+fail.
.It Ic fwdownload
Program firmware of the named SCSI device using the image file provided.
.Pp
@@ -1240,6 +1372,30 @@ camcontrol smpcmd ses0 -v -r 4 "40 0 00
Send the SMP REPORT GENERAL command to ses0, and display the number of PHYs
it contains.
Display SMP errors if the command fails.
+.Bd -literal -offset indent
+camcontrol security ada0
+.Ed
+.Pp
+Report security support and settings for ada0
+.Bd -literal -offset indent
+camcontrol security ada0 -u user -s MyPass
+.Ed
+.Pp
+Enable security on device ada0 with the password MyPass
+.Bd -literal -offset indent
+camcontrol security ada0 -u user -e MyPass
+.Ed
+.Pp
+Secure erase ada0 which has had security enabled with user password MyPass
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+This will
+.Em ERASE ALL
+data from the device, so backup your data before using!
+.Pp
+This command can be used used against an SSD drive to restoring it to
+factory default write performance.
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam_cdbparse 3 ,
Modified: stable/9/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.c Fri Jun 7 13:59:41 2013 (r251496)
+++ stable/9/sbin/camcontrol/camcontrol.c Fri Jun 7 14:10:18 2013 (r251497)
@@ -87,7 +87,8 @@ typedef enum {
CAM_CMD_SMP_PC = 0x00000019,
CAM_CMD_SMP_PHYLIST = 0x0000001a,
CAM_CMD_SMP_MANINFO = 0x0000001b,
- CAM_CMD_DOWNLOAD_FW = 0x0000001c
+ CAM_CMD_DOWNLOAD_FW = 0x0000001c,
+ CAM_CMD_SECURITY = 0x0000001d
} cam_cmdmask;
typedef enum {
@@ -140,6 +141,7 @@ static const char negotiate_opts[] = "ac
static const char smprg_opts[] = "l";
static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:";
static const char smpphylist_opts[] = "lq";
+static char pwd_opt;
#endif
static struct camcontrol_opts option_table[] = {
@@ -183,6 +185,7 @@ static struct camcontrol_opts option_tab
{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
+ {"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
#endif /* MINIMALISTIC */
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -274,7 +277,10 @@ static int scsireportluns(struct cam_dev
static int scsireadcapacity(struct cam_device *device, int argc, char **argv,
char *combinedopt, int retry_count, int timeout);
static int atapm(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int retry_count, int timeout);
+static int atasecurity(struct cam_device *device, int retry_count, int timeout,
+ int argc, char **argv, char *combinedopt);
+
#endif /* MINIMALISTIC */
#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
@@ -1328,55 +1334,93 @@ atacapprint(struct ata_params *parm)
printf("free-fall %s %s\n",
parm->support2 & ATA_SUPPORT_FREEFALL ? "yes" : "no",
parm->enabled2 & ATA_SUPPORT_FREEFALL ? "yes" : "no");
- printf("data set management (TRIM) %s\n",
- parm->support_dsm & ATA_SUPPORT_DSM_TRIM ? "yes" : "no");
+ printf("Data Set Management (DSM/TRIM) ");
+ if (parm->support_dsm & ATA_SUPPORT_DSM_TRIM) {
+ printf("yes\n");
+ printf("DSM - max 512byte blocks ");
+ if (parm->max_dsm_blocks == 0x00)
+ printf("yes not specified\n");
+ else
+ printf("yes %d\n",
+ parm->max_dsm_blocks);
+
+ printf("DSM - deterministic read ");
+ if (parm->support3 & ATA_SUPPORT_DRAT) {
+ if (parm->support3 & ATA_SUPPORT_RZAT)
+ printf("yes zeroed\n");
+ else
+ printf("yes any value\n");
+ } else {
+ printf("no\n");
+ }
+ } else {
+ printf("no\n");
+ }
}
static int
-ataidentify(struct cam_device *device, int retry_count, int timeout)
+scsi_cam_pass_16_send(struct cam_device *device, union ccb *ccb, int quiet)
{
- union ccb *ccb;
- struct ata_params *ident_buf;
- struct ccb_getdev cgd;
- u_int i, error = 0;
- int16_t *ptr;
+ struct ata_pass_16 *ata_pass_16;
+ struct ata_cmd ata_cmd;
- if (get_cgd(device, &cgd) != 0) {
- warnx("couldn't get CGD");
- return(1);
- }
- ccb = cam_getccb(device);
+ ata_pass_16 = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
+ ata_cmd.command = ata_pass_16->command;
+ ata_cmd.control = ata_pass_16->control;
+ ata_cmd.features = ata_pass_16->features;
- if (ccb == NULL) {
- warnx("couldn't allocate CCB");
- return(1);
+ if (arglist & CAM_ARG_VERBOSE) {
+ warnx("sending ATA %s via pass_16 with timeout of %u msecs",
+ ata_op_string(&ata_cmd),
+ ccb->csio.ccb_h.timeout);
}
- /* cam_getccb cleans up the header, caller has to zero the payload */
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
- ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
- if (ptr == NULL) {
- cam_freeccb(ccb);
- warnx("can't malloc memory for identify\n");
- return(1);
+ if (cam_send_ccb(device, ccb) < 0) {
+ if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+ warn("error sending ATA %s via pass_16",
+ ata_op_string(&ata_cmd));
+ }
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ return (1);
}
- bzero(ptr, sizeof(struct ata_params));
- cam_fill_ataio(&ccb->ataio,
- retry_count,
- NULL,
- /*flags*/CAM_DIR_IN,
- MSG_SIMPLE_Q_TAG,
- /*data_ptr*/(u_int8_t *)ptr,
- /*dxfer_len*/sizeof(struct ata_params),
- timeout ? timeout : 30 * 1000);
- if (cgd.protocol == PROTO_ATA)
- ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
- else
- ata_28bit_cmd(&ccb->ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0);
+ if (!(ata_pass_16->flags & AP_FLAG_CHK_COND) &&
+ (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+ warnx("ATA %s via pass_16 failed",
+ ata_op_string(&ata_cmd));
+ }
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ return (1);
+ }
+
+ return (0);
+}
+
+
+static int
+ata_cam_send(struct cam_device *device, union ccb *ccb, int quiet)
+{
+ if (arglist & CAM_ARG_VERBOSE) {
+ warnx("sending ATA %s with timeout of %u msecs",
+ ata_op_string(&(ccb->ataio.cmd)),
+ ccb->ataio.ccb_h.timeout);
+ }
/* Disable freezing the device queue */
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
@@ -1385,47 +1429,247 @@ ataidentify(struct cam_device *device, i
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
if (cam_send_ccb(device, ccb) < 0) {
- perror("error sending ATA identify");
+ if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+ warn("error sending ATA %s",
+ ata_op_string(&(ccb->ataio.cmd)));
+ }
if (arglist & CAM_ARG_VERBOSE) {
cam_error_print(device, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
}
- free(ptr);
- cam_freeccb(ccb);
- return(1);
+ return (1);
}
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- error = 1;
+ if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+ warnx("ATA %s failed: %d",
+ ata_op_string(&(ccb->ataio.cmd)), quiet);
+ }
if (arglist & CAM_ARG_VERBOSE) {
cam_error_print(device, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
}
+
+ return (1);
}
- cam_freeccb(ccb);
+ return (0);
+}
+
+static int
+ata_do_pass_16(struct cam_device *device, union ccb *ccb, int retries,
+ u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags,
+ u_int8_t tag_action, u_int8_t command, u_int8_t features,
+ u_int64_t lba, u_int8_t sector_count, u_int8_t *data_ptr,
+ u_int16_t dxfer_len, int timeout, int quiet)
+{
+ if (data_ptr != NULL) {
+ ata_flags |= AP_FLAG_BYT_BLOK_BYTES |
+ AP_FLAG_TLEN_SECT_CNT;
+ if (flags & CAM_DIR_OUT)
+ ata_flags |= AP_FLAG_TDIR_TO_DEV;
+ else
+ ata_flags |= AP_FLAG_TDIR_FROM_DEV;
+ } else {
+ ata_flags |= AP_FLAG_TLEN_NO_DATA;
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ scsi_ata_pass_16(&ccb->csio,
+ retries,
+ NULL,
+ flags,
+ tag_action,
+ protocol,
+ ata_flags,
+ features,
+ sector_count,
+ lba,
+ command,
+ /*control*/0,
+ data_ptr,
+ dxfer_len,
+ /*sense_len*/SSD_FULL_SIZE,
+ timeout);
+
+ return scsi_cam_pass_16_send(device, ccb, quiet);
+}
+
+static int
+ata_try_pass_16(struct cam_device *device)
+{
+ struct ccb_pathinq cpi;
+
+ if (get_cpi(device, &cpi) != 0) {
+ warnx("couldn't get CPI");
+ return (-1);
+ }
+
+ if (cpi.protocol == PROTO_SCSI) {
+ /* possibly compatible with pass_16 */
+ return (1);
+ }
+
+ /* likely not compatible with pass_16 */
+ return (0);
+}
+
+static int
+ata_do_28bit_cmd(struct cam_device *device, union ccb *ccb, int retries,
+ u_int32_t flags, u_int8_t protocol, u_int8_t tag_action,
+ u_int8_t command, u_int8_t features, u_int32_t lba,
+ u_int8_t sector_count, u_int8_t *data_ptr, u_int16_t dxfer_len,
+ int timeout, int quiet)
+{
+
+
+ switch (ata_try_pass_16(device)) {
+ case -1:
+ return (1);
+ case 1:
+ /* Try using SCSI Passthrough */
+ return ata_do_pass_16(device, ccb, retries, flags, protocol,
+ 0, tag_action, command, features, lba,
+ sector_count, data_ptr, dxfer_len,
+ timeout, quiet);
+ }
+
+ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) -
+ sizeof(struct ccb_hdr));
+ cam_fill_ataio(&ccb->ataio,
+ retries,
+ NULL,
+ flags,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ timeout);
+
+ ata_28bit_cmd(&ccb->ataio, command, features, lba, sector_count);
+ return ata_cam_send(device, ccb, quiet);
+}
+
+static void
+dump_data(uint16_t *ptr, uint32_t len)
+{
+ u_int i;
+
+ for (i = 0; i < len / 2; i++) {
+ if ((i % 8) == 0)
+ printf(" %3d: ", i);
+ printf("%04hx ", ptr[i]);
+ if ((i % 8) == 7)
+ printf("\n");
+ }
+ if ((i % 8) != 7)
+ printf("\n");
+}
+
+static int
+ata_do_identify(struct cam_device *device, int retry_count, int timeout,
+ union ccb *ccb, struct ata_params** ident_bufp)
+{
+ struct ata_params *ident_buf;
+ struct ccb_pathinq cpi;
+ struct ccb_getdev cgd;
+ u_int i, error;
+ int16_t *ptr;
+ u_int8_t command, retry_command;
+
+ if (get_cpi(device, &cpi) != 0) {
+ warnx("couldn't get CPI");
+ return (-1);
+ }
+
+ /* Neither PROTO_ATAPI or PROTO_SATAPM are used in cpi.protocol */
+ if (cpi.protocol == PROTO_ATA) {
+ if (get_cgd(device, &cgd) != 0) {
+ warnx("couldn't get CGD");
+ return (-1);
+ }
+
+ command = (cgd.protocol == PROTO_ATA) ?
+ ATA_ATA_IDENTIFY : ATA_ATAPI_IDENTIFY;
+ retry_command = 0;
+ } else {
+ /* We don't know which for sure so try both */
+ command = ATA_ATA_IDENTIFY;
+ retry_command = ATA_ATAPI_IDENTIFY;
+ }
+
+ ptr = (uint16_t *)calloc(1, sizeof(struct ata_params));
+ if (ptr == NULL) {
+ warnx("can't calloc memory for identify\n");
+ return (1);
+ }
+
+ error = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_IN,
+ /*protocol*/AP_PROTO_PIO_IN,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/command,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/(u_int8_t)sizeof(struct ata_params),
+ /*data_ptr*/(u_int8_t *)ptr,
+ /*dxfer_len*/sizeof(struct ata_params),
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/1);
if (error != 0) {
- free(ptr);
- return(error);
+ if (retry_command == 0) {
+ free(ptr);
+ return (1);
+ }
+ error = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_IN,
+ /*protocol*/AP_PROTO_PIO_IN,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/retry_command,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/(u_int8_t)
+ sizeof(struct ata_params),
+ /*data_ptr*/(u_int8_t *)ptr,
+ /*dxfer_len*/sizeof(struct ata_params),
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/0);
+
+ if (error != 0) {
+ free(ptr);
+ return (1);
+ }
}
- for (i = 0; i < sizeof(struct ata_params) / 2; i++)
+ error = 1;
+ for (i = 0; i < sizeof(struct ata_params) / 2; i++) {
ptr[i] = le16toh(ptr[i]);
+ if (ptr[i] != 0)
+ error = 0;
+ }
+
if (arglist & CAM_ARG_VERBOSE) {
fprintf(stdout, "%s%d: Raw identify data:\n",
device->device_name, device->dev_unit_num);
- for (i = 0; i < sizeof(struct ata_params) / 2; i++) {
- if ((i % 8) == 0)
- fprintf(stdout, " %3d: ", i);
- fprintf(stdout, "%04x ", (uint16_t)ptr[i]);
- if ((i % 8) == 7)
- fprintf(stdout, "\n");
- }
+ dump_data(ptr, sizeof(struct ata_params));
}
+
+ /* check for invalid (all zero) response */
+ if (error != 0) {
+ warnx("Invalid identify response detected");
+ free(ptr);
+ return (error);
+ }
+
ident_buf = (struct ata_params *)ptr;
if (strncmp(ident_buf->model, "FX", 2) &&
strncmp(ident_buf->model, "NEC", 3) &&
@@ -1446,15 +1690,636 @@ ataidentify(struct cam_device *device, i
ata_bpack(ident_buf->media_serial, ident_buf->media_serial,
sizeof(ident_buf->media_serial));
- fprintf(stdout, "%s%d: ", device->device_name,
- device->dev_unit_num);
+ *ident_bufp = ident_buf;
+
+ return (0);
+}
+
+
+static int
+ataidentify(struct cam_device *device, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ struct ata_params *ident_buf;
+
+ if ((ccb = cam_getccb(device)) == NULL) {
+ warnx("couldn't allocate CCB");
+ return (1);
+ }
+
+ if (ata_do_identify(device, retry_count, timeout, ccb, &ident_buf) != 0) {
+ cam_freeccb(ccb);
+ return (1);
+ }
+
+ printf("%s%d: ", device->device_name, device->dev_unit_num);
ata_print_ident(ident_buf);
camxferrate(device);
atacapprint(ident_buf);
free(ident_buf);
+ cam_freeccb(ccb);
- return(0);
+ return (0);
+}
+#endif /* MINIMALISTIC */
+
+
+#ifndef MINIMALISTIC
+enum {
+ ATA_SECURITY_ACTION_PRINT,
+ ATA_SECURITY_ACTION_FREEZE,
+ ATA_SECURITY_ACTION_UNLOCK,
+ ATA_SECURITY_ACTION_DISABLE,
+ ATA_SECURITY_ACTION_ERASE,
+ ATA_SECURITY_ACTION_ERASE_ENHANCED,
+ ATA_SECURITY_ACTION_SET_PASSWORD
+} atasecurity_action;
+
+static void
+atasecurity_print_time(u_int16_t tw)
+{
+
+ if (tw == 0)
+ printf("unspecified");
+ else if (tw >= 255)
+ printf("> 508 min");
+ else
+ printf("%i min", 2 * tw);
+}
+
+static u_int32_t
+atasecurity_erase_timeout_msecs(u_int16_t timeout)
+{
+
+ if (timeout == 0)
+ return 2 * 3600 * 1000; /* default: two hours */
+ else if (timeout > 255)
+ return (508 + 60) * 60 * 1000; /* spec says > 508 minutes */
+
+ return ((2 * timeout) + 5) * 60 * 1000; /* add a 5min margin */
+}
+
+
+static void
+atasecurity_notify(u_int8_t command, struct ata_security_password *pwd)
+{
+ struct ata_cmd cmd;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.command = command;
+ printf("Issuing %s", ata_op_string(&cmd));
+
+ if (pwd != NULL) {
+ char pass[sizeof(pwd->password)+1];
+
+ /* pwd->password may not be null terminated */
+ pass[sizeof(pwd->password)] = '\0';
+ strncpy(pass, pwd->password, sizeof(pwd->password));
+ printf(" password='%s', user='%s'",
+ pass,
+ (pwd->ctrl & ATA_SECURITY_PASSWORD_MASTER) ?
+ "master" : "user");
+
+ if (command == ATA_SECURITY_SET_PASSWORD) {
+ printf(", mode='%s'",
+ (pwd->ctrl & ATA_SECURITY_LEVEL_MAXIMUM) ?
+ "maximum" : "high");
+ }
+ }
+
+ printf("\n");
+}
+
+static int
+atasecurity_freeze(struct cam_device *device, union ccb *ccb,
+ int retry_count, u_int32_t timeout, int quiet)
+{
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_FREEZE_LOCK, NULL);
+
+ return ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_FREEZE_LOCK,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout,
+ /*quiet*/0);
+}
+
+static int
+atasecurity_unlock(struct cam_device *device, union ccb *ccb,
+ int retry_count, u_int32_t timeout,
+ struct ata_security_password *pwd, int quiet)
+{
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_UNLOCK, pwd);
+
+ return ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_OUT,
+ /*protocol*/AP_PROTO_PIO_OUT,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_UNLOCK,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/(u_int8_t *)pwd,
+ /*dxfer_len*/sizeof(*pwd),
+ /*timeout*/timeout,
+ /*quiet*/0);
+}
+
+static int
+atasecurity_disable(struct cam_device *device, union ccb *ccb,
+ int retry_count, u_int32_t timeout,
+ struct ata_security_password *pwd, int quiet)
+{
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_DISABLE_PASSWORD, pwd);
+ return ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_OUT,
+ /*protocol*/AP_PROTO_PIO_OUT,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_DISABLE_PASSWORD,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/(u_int8_t *)pwd,
+ /*dxfer_len*/sizeof(*pwd),
+ /*timeout*/timeout,
+ /*quiet*/0);
+}
+
+
+static int
+atasecurity_erase_confirm(struct cam_device *device,
+ struct ata_params* ident_buf)
+{
+
+ printf("\nYou are about to ERASE ALL DATA from the following"
+ " device:\n%s%d,%s%d: ", device->device_name,
+ device->dev_unit_num, device->given_dev_name,
+ device->given_unit_number);
+ ata_print_ident(ident_buf);
+
+ for(;;) {
+ char str[50];
+ printf("\nAre you SURE you want to ERASE ALL DATA? (yes/no) ");
+
+ if (fgets(str, sizeof(str), stdin) != NULL) {
+ if (strncasecmp(str, "yes", 3) == 0) {
+ return (1);
+ } else if (strncasecmp(str, "no", 2) == 0) {
+ return (0);
+ } else {
+ printf("Please answer \"yes\" or "
+ "\"no\"\n");
+ }
+ }
+ }
+
+ /* NOTREACHED */
+ return (0);
+}
+
+static int
+atasecurity_erase(struct cam_device *device, union ccb *ccb,
+ int retry_count, u_int32_t timeout,
+ u_int32_t erase_timeout,
+ struct ata_security_password *pwd, int quiet)
+{
+ int error;
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_ERASE_PREPARE, NULL);
+
+ error = ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_ERASE_PREPARE,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout,
+ /*quiet*/0);
+
+ if (error != 0)
+ return error;
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_ERASE_UNIT, pwd);
+
+ error = ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_OUT,
+ /*protocol*/AP_PROTO_PIO_OUT,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_ERASE_UNIT,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/(u_int8_t *)pwd,
+ /*dxfer_len*/sizeof(*pwd),
+ /*timeout*/erase_timeout,
+ /*quiet*/0);
+
+ if (error == 0 && quiet == 0)
+ printf("\nErase Complete\n");
+
+ return error;
+}
+
+static int
+atasecurity_set_password(struct cam_device *device, union ccb *ccb,
+ int retry_count, u_int32_t timeout,
+ struct ata_security_password *pwd, int quiet)
+{
+
+ if (quiet == 0)
+ atasecurity_notify(ATA_SECURITY_SET_PASSWORD, pwd);
+
+ return ata_do_28bit_cmd(device,
+ ccb,
+ retry_count,
+ /*flags*/CAM_DIR_OUT,
+ /*protocol*/AP_PROTO_PIO_OUT,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SECURITY_SET_PASSWORD,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/0,
+ /*data_ptr*/(u_int8_t *)pwd,
+ /*dxfer_len*/sizeof(*pwd),
+ /*timeout*/timeout,
+ /*quiet*/0);
+}
+
+static void
+atasecurity_print(struct ata_params *parm)
+{
+
+ printf("\nSecurity Option Value\n");
+ if (arglist & CAM_ARG_VERBOSE) {
+ printf("status %04x\n",
+ parm->security_status);
+ }
+ printf("supported %s\n",
+ parm->security_status & ATA_SECURITY_SUPPORTED ? "yes" : "no");
+ if (!(parm->security_status & ATA_SECURITY_SUPPORTED))
+ return;
+ printf("enabled %s\n",
+ parm->security_status & ATA_SECURITY_ENABLED ? "yes" : "no");
+ printf("drive locked %s\n",
+ parm->security_status & ATA_SECURITY_LOCKED ? "yes" : "no");
+ printf("security config frozen %s\n",
+ parm->security_status & ATA_SECURITY_FROZEN ? "yes" : "no");
+ printf("count expired %s\n",
+ parm->security_status & ATA_SECURITY_COUNT_EXP ? "yes" : "no");
+ printf("security level %s\n",
+ parm->security_status & ATA_SECURITY_LEVEL ? "maximum" : "high");
+ printf("enhanced erase supported %s\n",
+ parm->security_status & ATA_SECURITY_ENH_SUPP ? "yes" : "no");
+ printf("erase time ");
+ atasecurity_print_time(parm->erase_time);
+ printf("\n");
+ printf("enhanced erase time ");
+ atasecurity_print_time(parm->enhanced_erase_time);
+ printf("\n");
+ printf("master password rev %04x%s\n",
+ parm->master_passwd_revision,
+ parm->master_passwd_revision == 0x0000 ||
+ parm->master_passwd_revision == 0xFFFF ? " (unsupported)" : "");
+}
+
+/*
+ * Validates and copies the password in optarg to the passed buffer.
+ * If the password in optarg is the same length as the buffer then
+ * the data will still be copied but no null termination will occur.
+ */
+static int
+ata_getpwd(u_int8_t *passwd, int max, char opt)
+{
+ int len;
+
+ len = strlen(optarg);
+ if (len > max) {
+ warnx("-%c password is too long", opt);
+ return (1);
+ } else if (len == 0) {
+ warnx("-%c password is missing", opt);
+ return (1);
+ } else if (optarg[0] == '-'){
+ warnx("-%c password starts with '-' (generic arg?)", opt);
+ return (1);
+ } else if (strlen(passwd) != 0 && strcmp(passwd, optarg) != 0) {
+ warnx("-%c password conflicts with existing password from -%c",
+ opt, pwd_opt);
+ return (1);
+ }
+
+ /* Callers pass in a buffer which does NOT need to be terminated */
+ strncpy(passwd, optarg, max);
+ pwd_opt = opt;
+
+ return (0);
+}
+
+static int
+atasecurity(struct cam_device *device, int retry_count, int timeout,
+ int argc, char **argv, char *combinedopt)
+{
+ union ccb *ccb;
+ struct ata_params *ident_buf;
+ int error, confirm, quiet, c, action, actions, setpwd;
+ int security_enabled, erase_timeout, pwdsize;
+ struct ata_security_password pwd;
+
+ actions = 0;
+ setpwd = 0;
+ erase_timeout = 0;
+ confirm = 0;
+ quiet = 0;
+
+ memset(&pwd, 0, sizeof(pwd));
+
+ /* default action is to print security information */
+ action = ATA_SECURITY_ACTION_PRINT;
+
+ /* user is master by default as its safer that way */
+ pwd.ctrl |= ATA_SECURITY_PASSWORD_MASTER;
+ pwdsize = sizeof(pwd.password);
+
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-9
mailing list