diff -ruN ../HEAD/sbin/atacontrol/atacontrol.8 ./sbin/atacontrol/atacontrol.8 --- ../HEAD/sbin/atacontrol/atacontrol.8 2008-06-25 20:11:22.000000000 +0200 +++ ./sbin/atacontrol/atacontrol.8 2008-09-30 00:38:21.000000000 +0200 @@ -70,6 +70,10 @@ .Ic cap .Ar device .Nm +.Ic security +.Ar device +.Op Aq Ar security-command +.Nm .Ic spindown .Ar device .Op Ar seconds @@ -194,6 +198,62 @@ .It Ic cap Show detailed info about the device on .Ar device . +.It Ic security +If no security command given, show details on the security status of the +.Ar device . +The following security commands are available: +.Bl -tag -width "1234567" +.It Ic freeze +Freeze the security configuration of the device. +Once frozen, no other security commands are accepted by the device. +The device can only be unfrozen by a power cycle. +An ATA Security compliant BIOS should freeze devices before booting the +operating system. +If your BIOS does not freeze the security configuration, you can freeze +it after boot by adding an appropriate freeze command to e.g.\& +.Pa /etc/rc.local . +.It Ic set master +Set the master password on the device and decrement the master password +revision, if supported by the device. +Setting the master password does not enable security. +The master password serves as a fallback password to make a locked device +usable again if the user password is lost. +.It Ic set user Bro Ic high | maximum Brc +Set the user password and enable security. +With security enabled, the device will be locked after power-on. +In high security mode, either the user or the master password can +unlock the device. +In maximum security mode, only the user password can unlock the device. +If the user password is lost, the master password can only be used to +erase the device in order to make it usable again. +.It Ic unlock Bro Ic master | user Brc +Unlock the device using either the master or the user password. +Unlocking will make the device available for use by the system. +The drive can only be unlocked by a master password if the security +mode is high. Unlocking does not disable security; after a reboot, +the device will be locked again. +.It Ic disable Bro Ic master | user Brc +Disable security using either the master or the user password. +By disabling security, the user password will be removed and +the device will no longer be locked after power-on. The master +password is unaffected by the disable command. +.It Ic erase Bro Ic master | user Brc Op Ic enhanced +Completely erase all blocks on the device. +This will destroy all data on disk and then disable security. +Erasing the device using the master password is the only way +to recover a secured device in maximum security mode if the +user password is lost. +In normal erase mode, all addressable blocks are overwritten with zero. +In enhanced erase mode, all blocks, including previously reallocated +bad blocks, are overwritten with some predetermined pattern. +Enhanced erase mode is not supported by all devices. +Erasing a device will take a long time. +Recent devices report the estimated erase time as part of the security +status information. +.El +.Pp +.Em WARNING : +The security commands can render a disk useless! .It Ic spindown Set or report timeout after which the .Ar device @@ -368,6 +428,9 @@ utility was written by .An S\(/oren Schmidt .Aq sos@FreeBSD.org . +Support for ATA Security was added by +.An Daniel Roethlisberger +.Aq daniel@roe.ch . .Pp This manual page was written by .An S\(/oren Schmidt diff -ruN ../HEAD/sbin/atacontrol/atacontrol.c ./sbin/atacontrol/atacontrol.c --- ../HEAD/sbin/atacontrol/atacontrol.c 2008-08-06 20:08:02.000000000 +0200 +++ ./sbin/atacontrol/atacontrol.c 2008-09-30 01:01:57.000000000 +0200 @@ -103,6 +103,7 @@ " atacontrol status array\n" " atacontrol mode device [mode]\n" " atacontrol cap device\n" + " atacontrol security device []\n" " atacontrol spindown device [seconds]\n" ); exit(EX_USAGE); @@ -122,6 +123,18 @@ } static void +ata_request(int fd, struct ata_ioc_request *req) +{ + if (ioctl(fd, IOCATAREQUEST, req) < 0) + err(1, "ioctl(IOCATAREQUEST)"); + + if (req->error) { + fprintf(stderr, "atacontrol: ATA request failed (0x%04x)\n", req->error); + exit(EX_IOERR); + } +} + +static void param_print(struct ata_params *parm) { printf("<%.40s/%.8s> ", parm->model, parm->revision); @@ -309,6 +322,235 @@ } } +static void +security_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 void +security_print(struct ata_params *parm) +{ + printf("\n"); +#if 0 + printf("Security status %04x\n", parm->security_status); +#endif + printf("Security supported %s\n", + parm->security_status & ATA_SECURITY_SUPPORTED ? "yes" : "no"); + if (!(parm->security_status & ATA_SECURITY_SUPPORTED)) + return; + printf("Security 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 "); + security_print_time(parm->erase_time); + printf("\n"); + printf("Enhanced erase time "); + security_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)" : ""); +} + +static void +security_ask_password(char *pw, size_t len, int master, int confirm) +{ + char *input; + + input = getpass(master ? "Master password:" : "User password:"); + if (!input) { + err(1, "getpass()"); + } + if (strlen(input) > len) { + fprintf(stderr, "atacontrol: Password too long\n"); + exit(EX_USAGE); + } + strlcpy(pw, input, len); + if (!confirm) + return; + input = getpass(master ? "Retype master password:" : + "Retype user password:"); + if (!input) { + err(1, "getpass()"); + } + if (strcmp(pw, input)) { + fprintf(stderr, "atacontrol: Password mismatch\n"); + exit(EX_DATAERR); + } +} + +static u_int16_t +security_get_revision(int fd) +{ + struct ata_params params; + + if (ioctl(fd, IOCATAGPARM, ¶ms) < 0) + err(1, "ioctl(IOCATAGPARM)"); + return params.master_passwd_revision; +} + +/* + * security + */ +static void +ata_security_print(int fd) +{ + struct ata_params params; + + if (ioctl(fd, IOCATAGPARM, ¶ms) < 0) + err(1, "ioctl(IOCATAGPARM)"); + security_print(¶ms); +} + +/* + * security freeze + */ +static void +ata_security_freeze(int fd) +{ + struct ata_ioc_request req; + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_FREEZE_LOCK; + req.timeout = 1000; + req.flags = ATA_CMD_CONTROL; + ata_request(fd, &req); +} + +/* + * security set master + * security set user high + * security set user maximum + */ +static void +ata_security_set(int fd, u_int16_t ctrl) +{ + struct ata_ioc_request req; + struct ata_security_password pwd; + + memset(&pwd, 0, sizeof(pwd)); + pwd.ctrl = ctrl; + + if (ctrl & ATA_SECURITY_PASSWORD_MASTER) { + pwd.revision = security_get_revision(fd); + if (pwd.revision != 0 && pwd.revision != 0xffff && + --pwd.revision == 0) { + pwd.revision = 0xfffe; + } + } + + security_ask_password((char*)&pwd.password, sizeof(pwd.password), + ctrl & ATA_SECURITY_PASSWORD_MASTER, 1); + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_SET_PASSWORD; + req.timeout = 1000; + req.flags = ATA_CMD_WRITE; + req.data = (caddr_t)&pwd; + req.count = sizeof(pwd); + ata_request(fd, &req); +} + +/* + * security unlock master + * security unlock user + */ +static void +ata_security_unlock(int fd, u_int16_t ctrl) +{ + struct ata_ioc_request req; + struct ata_security_password pwd; + + memset(&pwd, 0, sizeof(pwd)); + pwd.ctrl = ctrl; + + security_ask_password((char*)&pwd.password, sizeof(pwd.password), + ctrl & ATA_SECURITY_PASSWORD_MASTER, 0); + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_UNLOCK; + req.timeout = 1000; + req.flags = ATA_CMD_WRITE; + req.data = (caddr_t)&pwd; + req.count = sizeof(pwd); + ata_request(fd, &req); +} + +/* + * security disable master + * security disable user + */ +static void +ata_security_disable(int fd, u_int16_t ctrl) +{ + struct ata_ioc_request req; + struct ata_security_password pwd; + + memset(&pwd, 0, sizeof(pwd)); + pwd.ctrl = ctrl; + + security_ask_password((char*)&pwd.password, sizeof(pwd.password), + ctrl & ATA_SECURITY_PASSWORD_MASTER, 0); + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_DISABLE_PASSWORD; + req.timeout = 1000; + req.flags = ATA_CMD_WRITE; + req.data = (caddr_t)&pwd; + req.count = sizeof(pwd); + ata_request(fd, &req); +} + +/* + * security erase master + * security erase master enhanced + * security erase user + * security erase user enhanced + */ +static void +ata_security_erase(int fd, u_int16_t ctrl) +{ + struct ata_ioc_request req; + struct ata_security_password pwd; + + memset(&pwd, 0, sizeof(pwd)); + pwd.ctrl = ctrl; + + security_ask_password((char*)&pwd.password, sizeof(pwd.password), + ctrl & ATA_SECURITY_PASSWORD_MASTER, 0); + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_ERASE_PREPARE; + req.timeout = 1000; + req.flags = ATA_CMD_CONTROL; + ata_request(fd, &req); + + memset(&req, 0, sizeof(req)); + req.u.ata.command = ATA_SECURITY_ERASE_UNIT; + req.timeout = 100000; /* takes literally hours to complete */ + req.flags = ATA_CMD_WRITE; + req.data = (caddr_t)&pwd; + req.count = sizeof(pwd); + ata_request(fd, &req); +} + static int open_dev(const char *arg, int mode) { @@ -386,6 +628,95 @@ exit(EX_OK); } + if (!strcmp(argv[1], "security") && argc >= 3 && argc <= 6) { + u_int16_t ctrl = 0; + + fd = open_dev(argv[2], O_RDONLY); + if (argc == 3) { + ata_security_print(fd); + exit(EX_OK); + } else if (argc == 4 && !strcmp(argv[3], "freeze")) { + ata_security_freeze(fd); + exit(EX_OK); + } else if (argc >= 5 && !strcmp(argv[3], "set")) { + if (argc == 5 && !strcmp(argv[4], "master")) { + ctrl |= ATA_SECURITY_PASSWORD_MASTER; + } else if (argc == 6 && !strcmp(argv[4], "user")) { + ctrl |= ATA_SECURITY_PASSWORD_USER; + if (!strcmp(argv[5], "high")) + ctrl |= ATA_SECURITY_LEVEL_HIGH; + else if (!strcmp(argv[5], "maximum")) + ctrl |= ATA_SECURITY_LEVEL_MAXIMUM; + else { + fprintf(stderr, "atacontrol: " + "Level must be " + "high or maximum\n"); + exit(EX_USAGE); + } + } else { + fprintf(stderr, "atacontrol: " + "Password must be " + "master or user, " + "user requires level\n"); + exit(EX_USAGE); + } + ata_security_set(fd, ctrl); + exit(EX_OK); + } else if (argc == 5 && !strcmp(argv[3], "unlock")) { + if (!strcmp(argv[4], "master")) + ctrl |= ATA_SECURITY_PASSWORD_MASTER; + else if (!strcmp(argv[4], "user")) + ctrl |= ATA_SECURITY_PASSWORD_USER; + else { + fprintf(stderr, "atacontrol: " + "Password must be " + "master or user\n"); + exit(EX_USAGE); + } + ata_security_unlock(fd, ctrl); + exit(EX_OK); + } else if (argc == 5 && !strcmp(argv[3], "disable")) { + if (!strcmp(argv[4], "master")) + ctrl |= ATA_SECURITY_PASSWORD_MASTER; + else if (!strcmp(argv[4], "user")) + ctrl |= ATA_SECURITY_PASSWORD_USER; + else { + fprintf(stderr, "atacontrol: " + "Password must be " + "master or user\n"); + exit(EX_USAGE); + } + ata_security_disable(fd, ctrl); + exit(EX_OK); + } else if (argc >= 5 && !strcmp(argv[3], "erase")) { + if (!strcmp(argv[4], "master")) + ctrl |= ATA_SECURITY_PASSWORD_MASTER; + else if (!strcmp(argv[4], "user")) + ctrl |= ATA_SECURITY_PASSWORD_USER; + else { + fprintf(stderr, "atacontrol: " + "Password must be " + "master or user\n"); + exit(EX_USAGE); + } + if (argc == 5) + ctrl |= ATA_SECURITY_ERASE_NORMAL; + else if (!strcmp(argv[5], "enhanced")) + ctrl |= ATA_SECURITY_ERASE_ENHANCED; + else { + fprintf(stderr, "atacontrol: " + "Invalid argument\n"); + exit(EX_USAGE); + } + ata_security_erase(fd, ctrl); + exit(EX_OK); + } + fprintf(stderr, "atacontrol: " + "Invalid security command or " + "wrong number of arguments\n"); + exit(EX_USAGE); + } + if ((fd = open("/dev/ata", O_RDWR)) < 0) err(1, "control device not found"); diff -ruN ../HEAD/sys/sys/ata.h ./sys/sys/ata.h --- ../HEAD/sys/sys/ata.h 2008-04-10 15:01:17.000000000 +0200 +++ ./sys/sys/ata.h 2008-09-28 21:53:56.000000000 +0200 @@ -170,10 +170,10 @@ } __packed support, enabled; /*088*/ u_int16_t udmamodes; /* UltraDMA modes */ -/*089*/ u_int16_t erase_time; -/*090*/ u_int16_t enhanced_erase_time; +/*089*/ u_int16_t erase_time; /* time req'd in 2min units */ +/*090*/ u_int16_t enhanced_erase_time; /* time req'd in 2min units */ /*091*/ u_int16_t apm_value; -/*092*/ u_int16_t master_passwd_revision; +/*092*/ u_int16_t master_passwd_revision; /* password revision code */ /*093*/ u_int16_t hwres; #define ATA_CABLE_ID 0x2000 @@ -191,7 +191,16 @@ u_int16_t lba_size48_4; u_int16_t reserved104[23]; /*127*/ u_int16_t removable_status; + /*128*/ u_int16_t security_status; +#define ATA_SECURITY_LEVEL 0x0100 /* 0: high, 1: maximum */ +#define ATA_SECURITY_ENH_SUPP 0x0020 /* enhanced erase supported */ +#define ATA_SECURITY_COUNT_EXP 0x0010 /* count expired */ +#define ATA_SECURITY_FROZEN 0x0008 /* security config is frozen */ +#define ATA_SECURITY_LOCKED 0x0004 /* drive is locked */ +#define ATA_SECURITY_ENABLED 0x0002 /* ATA Security is enabled */ +#define ATA_SECURITY_SUPPORTED 0x0001 /* ATA Security is supported */ + u_int16_t reserved129[31]; /*160*/ u_int16_t cfa_powermode1; u_int16_t reserved161[15]; @@ -283,7 +292,12 @@ #define ATA_SF_DIS_RELIRQ 0xdd /* disable release interrupt */ #define ATA_SF_ENAB_SRVIRQ 0x5e /* enable service interrupt */ #define ATA_SF_DIS_SRVIRQ 0xde /* disable service interrupt */ -#define ATA_SECURITY_FREEE_LOCK 0xf5 /* freeze security config */ +#define ATA_SECURITY_SET_PASSWORD 0xf1 /* set drive password */ +#define ATA_SECURITY_UNLOCK 0xf2 /* unlock drive using passwd */ +#define ATA_SECURITY_ERASE_PREPARE 0xf3 /* prepare to erase drive */ +#define ATA_SECURITY_ERASE_UNIT 0xf4 /* erase all blocks on drive */ +#define ATA_SECURITY_FREEZE_LOCK 0xf5 /* freeze security config */ +#define ATA_SECURITY_DISABLE_PASSWORD 0xf6 /* disable drive password */ #define ATA_READ_NATIVE_MAX_ADDRESS 0xf8 /* read native max address */ #define ATA_SET_MAX_ADDRESS 0xf9 /* set max address */ @@ -429,6 +443,20 @@ int error; }; +struct ata_security_password { + u_int16_t ctrl; +#define ATA_SECURITY_PASSWORD_USER 0x0000 +#define ATA_SECURITY_PASSWORD_MASTER 0x0001 +#define ATA_SECURITY_ERASE_NORMAL 0x0000 +#define ATA_SECURITY_ERASE_ENHANCED 0x0002 +#define ATA_SECURITY_LEVEL_HIGH 0x0000 +#define ATA_SECURITY_LEVEL_MAXIMUM 0x0100 + + u_int8_t password[32]; + u_int16_t revision; + u_int16_t reserved[238]; +}; + /* pr device ATA ioctl calls */ #define IOCATAREQUEST _IOWR('a', 100, struct ata_ioc_request) #define IOCATAGPARM _IOR('a', 101, struct ata_params)