Author: smh
Date: Fri Jun  7 13:30:59 2013
New Revision: 251494
URL: http://svnweb.freebsd.org/changeset/base/251494

Log:
  MFC r249115:
  Adds security command to camcontrol which provides the ability to secure erase
  SSD's

Modified:
  stable/8/sbin/camcontrol/camcontrol.8
  stable/8/sbin/camcontrol/camcontrol.c
  stable/8/sys/sys/ata.h
Directory Properties:
  stable/8/sbin/camcontrol/   (props changed)
  stable/8/sys/   (props changed)
  stable/8/sys/sys/   (props changed)

Modified: stable/8/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/8/sbin/camcontrol/camcontrol.8       Fri Jun  7 12:42:49 2013        
(r251493)
+++ stable/8/sbin/camcontrol/camcontrol.8       Fri Jun  7 13:30:59 2013        
(r251494)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 4, 2012
+.Dd June 7, 2013
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -184,6 +184,21 @@
 .Op device id
 .Op generic args
 .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
@@ -856,6 +871,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 help
 Print out verbose usage information.
 .El
@@ -974,6 +1106,30 @@ camcontrol negotiate -n da -u 3 -R 20.00
 Negotiate a sync rate of 20MHz and an offset of 15 with da3.
 Then send a
 Test Unit Ready command to make the settings take effect.
+.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/8/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/8/sbin/camcontrol/camcontrol.c       Fri Jun  7 12:42:49 2013        
(r251493)
+++ stable/8/sbin/camcontrol/camcontrol.c       Fri Jun  7 13:30:59 2013        
(r251494)
@@ -77,7 +77,8 @@ typedef enum {
        CAM_CMD_IDENTIFY        = 0x00000013,
        CAM_CMD_IDLE            = 0x00000014,
        CAM_CMD_STANDBY         = 0x00000015,
-       CAM_CMD_SLEEP           = 0x00000016
+       CAM_CMD_SLEEP           = 0x00000016,
+       CAM_CMD_SECURITY        = 0x00000017
 } cam_cmdmask;
 
 typedef enum {
@@ -127,6 +128,7 @@ struct camcontrol_opts {
 static const char scsicmd_opts[] = "a:c:dfi:o:r";
 static const char readdefect_opts[] = "f:GP";
 static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
+static char pwd_opt;
 #endif
 
 struct camcontrol_opts option_table[] = {
@@ -161,6 +163,7 @@ struct camcontrol_opts option_table[] = 
        {"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
        {"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
        {"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
+       {"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},
@@ -225,7 +228,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))
@@ -1260,55 +1266,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;
@@ -1317,47 +1361,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) &&
@@ -1378,15 +1622,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;
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to