HPA_DCO_1 - Add HPA, DCO capability

1. nwipe now reads the HPA set and real number of
sectors and the DSO real max sectors. It determines
from these values whether the HPA is enabled, disabled
or in a unknown state. The values are logged to nwipes
log and flags are set in the drives context.

More code will follow that adds this information to the PDF
certificate and to the drive selection window in the GUI.
This commit is contained in:
PartialVolume
2023-02-26 22:59:33 +00:00
parent 00e2053ab4
commit bba050fa71
7 changed files with 465 additions and 27 deletions

View File

@@ -6,5 +6,5 @@ AM_LDFLAGS =
# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile
bin_PROGRAMS = nwipe
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c
nwipe_LDADD = $(PARTED_LIBS)

View File

@@ -159,8 +159,12 @@ typedef struct nwipe_context_t_
char PDF_filename[256]; // The filename of the PDF certificate/report.
int HPA_pre_erase_status; // 0 = No HPA found/disabled, 1 = HPA detected, 2 = Unknown, unable to checked
int HPA_post_erase_status; // 0 = No HPA found/disabled, 1 = HPA detected, 2 = Unknown, unable to checked
u64 HPA_reported_set; // the 'HPA set' value reported hdparm -N, i.e the first value of n/n
u64 HPA_reported_real; // the 'HPA real' value reported hdparm -N, i.e the second value of n/n
int DCO_pre_erase_status; // 0 = No DCO found, 1 = DCO detected, 2 = Unknown, unable to checked
int DCO_post_erase_status; // 0 = No DCO found, 1 = DCO detected, 2 = Unknown, unable to checked
u64 DCO_reported_real_max_sectors; // real max sectors as reported by hdparm --dco-identify
/*
* Identity contains the raw serial number of the drive
* (where applicable), however, for use within nwipe use the

View File

@@ -340,7 +340,6 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
check_HPA = 1;
break;
}
if( check_HPA == 1 )
{
hpa_dco_status( next_device, PRE_WIPE_HPA_CHECK );

View File

@@ -35,6 +35,17 @@
#include "logging.h"
#include "options.h"
#include "hpa_dco.h"
#include "miscellaneous.h"
/* This function makes use of the hdparm program to determine HPA/DCO status. I would have prefered
* to write this function using low level access to the hardware however I would have needed
* to understand fully the various edge cases that would need be be dealt with. As I need to add
* HPA/detection to nwipe as quickly as possible I decided it would just be quicker to utilise hdparm
* rather than reinvent the wheel. However, I don't like doing it like this as a change in formatted
* output of hdparm could potentially break HPA/DCO detection requiring a fix. Anybody that wants to
* re-write this function for a purer nwipe without the use of hdparm then by all means please go
* ahead and submit a pull request to https://github.com/martijnvanbrummelen/nwipe
*/
int hpa_dco_status( nwipe_context_t* ptr, int pre_or_post )
{
@@ -44,17 +55,37 @@ int hpa_dco_status( nwipe_context_t* ptr, int pre_or_post )
int r; // A result buffer.
int set_return_value;
int exit_status;
int hpa_line_found;
int dco_line_found;
FILE* fp;
char hdparm_command[] = "hdparm -N";
char hdparm_command2[] = "/sbin/hdparm -N";
char hdparm_command3[] = "/usr/bin/hdparm -N";
char path_hdparm_cmd1_get_hpa[] = "hdparm -N";
char path_hdparm_cmd2_get_hpa[] = "/sbin/hdparm -N";
char path_hdparm_cmd3_get_hpa[] = "/usr/bin/hdparm -N";
char path_hdparm_cmd4_get_dco[] = "hdparm --dco-identify";
char path_hdparm_cmd5_get_dco[] = "/sbin/hdparm --dco-identify";
char path_hdparm_cmd6_get_dco[] = "/usr/bin/hdparm --dco-identify";
char result[512];
char final_cmd_hdparm[sizeof( hdparm_command3 ) + sizeof( c->device_name )];
char* p;
/* Use the longest of the 'path_hdparm_cmd.....' strings above to
*determine size in the strings below
*/
char hdparm_cmd_get_hpa[sizeof( path_hdparm_cmd3_get_hpa ) + sizeof( c->device_name )];
char hdparm_cmd_get_dco[sizeof( path_hdparm_cmd6_get_dco ) + sizeof( c->device_name )];
/* Initialise return value */
set_return_value = 0;
/* Construct the command including path to the binary if required, I do it like this to cope
* with distros that don't setup their paths in a standard way or maybe don't even define a
* path. By doing this we avoid the 'No such file or directory' message you would otherwise
* get on some distros. -> debian SID
*/
if( system( "which hdparm > /dev/null 2>&1" ) )
{
if( system( "which /sbin/hdparm > /dev/null 2>&1" ) )
@@ -70,67 +101,148 @@ int hpa_dco_status( nwipe_context_t* ptr, int pre_or_post )
}
else
{
snprintf( final_cmd_hdparm, sizeof( final_cmd_hdparm ), "%s %s\n", hdparm_command3, c->device_name );
snprintf( hdparm_cmd_get_hpa,
sizeof( hdparm_cmd_get_hpa ),
"%s %s\n",
path_hdparm_cmd3_get_hpa,
c->device_name );
snprintf( hdparm_cmd_get_dco,
sizeof( hdparm_cmd_get_dco ),
"%s %s\n",
path_hdparm_cmd6_get_dco,
c->device_name );
}
}
else
{
snprintf( final_cmd_hdparm, sizeof( final_cmd_hdparm ), "%s %s", hdparm_command2, c->device_name );
snprintf(
hdparm_cmd_get_hpa, sizeof( hdparm_cmd_get_hpa ), "%s %s", path_hdparm_cmd2_get_hpa, c->device_name );
snprintf(
hdparm_cmd_get_dco, sizeof( hdparm_cmd_get_dco ), "%s %s\n", path_hdparm_cmd5_get_dco, c->device_name );
}
}
else
{
snprintf( final_cmd_hdparm, sizeof( final_cmd_hdparm ), "%s %s", hdparm_command, c->device_name );
snprintf( hdparm_cmd_get_hpa, sizeof( hdparm_cmd_get_hpa ), "%s %s", path_hdparm_cmd1_get_hpa, c->device_name );
snprintf(
hdparm_cmd_get_dco, sizeof( hdparm_cmd_get_dco ), "%s %s\n", path_hdparm_cmd4_get_dco, c->device_name );
}
nwipe_log( NWIPE_LOG_DEBUG, "hpa_dco_status: hdparm command %s", final_cmd_hdparm );
/* Initialise the results buffer, so we don't some how inadvertently process a past result */
memset( result, 0, sizeof( result ) );
if( final_cmd_hdparm[0] != 0 )
if( hdparm_cmd_get_hpa[0] != 0 )
{
fp = popen( final_cmd_hdparm, "r" );
fp = popen( hdparm_cmd_get_hpa, "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", final_cmd_hdparm );
nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_hpa );
set_return_value = 1;
}
if( fp != NULL )
{
hpa_line_found = 0; //* init */
/* Read the output a line at a time - output it. */
while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", final_cmd_hdparm, result );
nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_hpa, result );
}
/* Change the output of hdparm to lower case and search using lower case strings, to try
* to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection
*/
strlower( result ); // convert the result to lower case
/* Scan the hdparm results for HPA is disabled
*/
if( pre_or_post == PRE_WIPE_HPA_CHECK )
if( strstr( result, "hpa is disabled" ) != 0 )
{
if( strstr( result, "HPA is disabled" ) != 0 )
if( pre_or_post == PRE_WIPE_HPA_CHECK )
{
c->HPA_pre_erase_status = HPA_DISABLED;
nwipe_log( NWIPE_LOG_INFO, "[GOOD]The host protected area is disabled on %s", c->device_name );
}
else
{
if( strstr( result, "HPA is enabled" ) != 0 )
c->HPA_post_erase_status = HPA_DISABLED;
}
nwipe_log( NWIPE_LOG_INFO, "[GOOD] The host protected area is disabled on %s", c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "hpa is enabled" ) != 0 )
{
c->HPA_pre_erase_status = HPA_ENABLED;
nwipe_log(
NWIPE_LOG_WARNING, "[BAD] The host protected area is enabled on %s", c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "invalid" ) != 0 )
{
c->HPA_pre_erase_status = HPA_ENABLED;
nwipe_log(
NWIPE_LOG_WARNING, "[BAD]The host protected area is enabled on %s", c->device_name );
}
else
{
c->HPA_pre_erase_status = HPA_UNKNOWN;
nwipe_log( NWIPE_LOG_WARNING,
"[UNSURE] hdparm reports invalid output, buggy drive firmware on %s?",
c->device_name );
// We'll assume the HPA values are in the string as we may be able to extract something
// meaningful
hpa_line_found = 1;
break;
}
}
}
}
/* if the line was found that contains hpa is enabled or disabled message
* then process the line, extracting the 'hpa set' and 'hpa real' values.
*/
if( hpa_line_found == 1 )
{
/* Extract the 'HPA set' value, the first value in the line and convert
* to binary and save in context */
c->HPA_reported_set = str_ascii_number_to_ll( result );
/* Extract the 'HPA real' value, the second value in the line and convert
* to binary and save in context, this is a little more difficult as sometimes
* a odd value is returned so instead of nnnnn/nnnnn you get nnnnnn/1(nnnnnn).
* So first we scan for a open bracket '(' then if there is no '(' we then start the
* search immediately after the '/'.
*/
if( ( p = strstr( result, "(" ) ) )
{
c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
}
else
{
if( ( p = strstr( result, "/" ) ) )
{
c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
}
}
nwipe_log( NWIPE_LOG_INFO,
"HPA values %lli / %lli on %s",
c->HPA_reported_set,
c->HPA_reported_real,
c->device_name );
}
else
{
c->HPA_pre_erase_status = HPA_UNKNOWN;
nwipe_log( NWIPE_LOG_WARNING,
"[UNKNOWN] We can't find the HPA line, has hdparm ouput changed? %s",
c->device_name );
}
/* close */
r = pclose( fp );
if( r > 0 )
@@ -140,7 +252,7 @@ int hpa_dco_status( nwipe_context_t* ptr, int pre_or_post )
{
nwipe_log( NWIPE_LOG_WARNING,
"hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
final_cmd_hdparm,
hdparm_cmd_get_hpa,
exit_status );
}
@@ -156,5 +268,160 @@ int hpa_dco_status( nwipe_context_t* ptr, int pre_or_post )
}
}
}
/* Initialise the results buffer again, so we don't
* some how inadvertently process a past result */
memset( result, 0, sizeof( result ) );
/* -----------------------------------------------
* Run the dco identify command and determine the
* real max sectors, store it in the drive context
* for comparison against the hpa reported drive
* size values.
*/
dco_line_found = 0;
if( hdparm_cmd_get_dco[0] != 0 )
{
fp = popen( hdparm_cmd_get_dco, "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_dco );
set_return_value = 1;
}
if( fp != NULL )
{
/* Read the output a line at a time - output it. */
while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
{
/* Change the output of hdparm to lower case and search using lower case strings, to try
* to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection */
strlower( result );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_dco, result );
}
if( strstr( result, "real max sectors" ) != 0 )
{
/* extract the real max sectors, convert to binary and store in drive context */
dco_line_found = 1;
break;
}
}
/* DCO line found, now process it */
if( dco_line_found == 1 )
{
c->DCO_reported_real_max_sectors = str_ascii_number_to_ll( result );
nwipe_log( NWIPE_LOG_INFO,
"DCO Real max sectors reported as %lli on %s",
c->DCO_reported_real_max_sectors,
c->device_name );
}
else
{
c->DCO_reported_real_max_sectors = 0;
nwipe_log( NWIPE_LOG_INFO, "DCO Real max sectors not found" );
}
/* close */
r = pclose( fp );
if( r > 0 )
{
exit_status = WEXITSTATUS( r );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_WARNING,
"hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
hdparm_cmd_get_dco,
exit_status );
}
if( exit_status == 127 )
{
nwipe_log( NWIPE_LOG_WARNING, "Command not found. Installing hdparm is mandatory !" );
set_return_value = 2;
if( nwipe_options.nousb )
{
return set_return_value;
}
}
}
}
}
/* Compare the results of hdparm -N (HPA set / HPA real)
* and hdparm --dco-identidy (real max sectors). All three
* values may be different or perhaps 'HPA set' and 'HPA real' are
* different and 'HPA real' matches 'real max sectors'.
*
* A perfect HPA disabled result would be where all three
* values are the same. It can then be considered that the
* HPA is disabled.
*
* If 'HPA set' and 'HPA real' are different then it
* can be considered that HPA is enabled)
*/
/* Determine, based on the values of 'HPA set', 'HPA real,
* and 'real max sectors' whether we set the HPA
* flag as HPA_DISABLED, HPA_ENABLED or HPA_UNKNOWN.
* The HPA flag will be displayed in the GUI and on
* the certificate and is used to determine whether
* to reset the HPA.
*/
/* If all three values match then there is no hidden disc area. HPA is disabled. */
if( ( c->HPA_reported_set == c->HPA_reported_real ) && c->DCO_reported_real_max_sectors == c->HPA_reported_set )
{
c->HPA_pre_erase_status = HPA_DISABLED;
}
else
{
/* If HPA set and DCO max sectors are equal it can also be considered that HPA is disabled */
if( c->HPA_reported_set == c->DCO_reported_real_max_sectors )
{
c->HPA_pre_erase_status = HPA_DISABLED;
}
else
{
if( c->HPA_reported_set != c->DCO_reported_real_max_sectors )
{
c->HPA_pre_erase_status = HPA_ENABLED;
}
}
}
if( c->HPA_pre_erase_status == HPA_DISABLED )
{
nwipe_log( NWIPE_LOG_INFO, "[GOOD] HPA is disabled on %s", c->device_name );
}
else
{
if( c->HPA_pre_erase_status == HPA_ENABLED )
{
nwipe_log( NWIPE_LOG_WARNING, "[BAD] HPA is enabled on %s", c->device_name );
}
else
{
if( c->HPA_pre_erase_status == HPA_UNKNOWN )
{
nwipe_log(
NWIPE_LOG_WARNING, "[UNKNOWN] We can't seem to determine the HPA status on %s", c->device_name );
}
}
}
/* Determine the size of the HPA and store the results in the
* context.
*/
// WARNING Add code here
return set_return_value;
}

113
src/miscellaneous.c Normal file
View File

@@ -0,0 +1,113 @@
/*
* miscellaneous.c: functions that may be generally used throughout nwipes code,
* mainly string processing related functions.
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "nwipe.h"
/* Convert string to upper case
*/
void strupper( char* str )
{
int idx;
idx = 0;
while( str[idx] != 0 )
{
/* If upper case alpha character, change to lower case */
if( str[idx] >= 'A' && str[idx] <= 'Z' )
{
str[idx] -= 32;
}
idx++;
}
}
/* Convert string to lower case
*/
void strlower( char* str )
{
int idx;
idx = 0;
while( str[idx] != 0 )
{
/* If upper case alpha character, change to lower case */
if( str[idx] >= 'A' && str[idx] <= 'Z' )
{
str[idx] += 32;
}
idx++;
}
}
/* Search a string for a positive number, convert the first
* number found to binary and return the binary number.
* returns the number or -1 if number too large or -2 if
* no number found.
*/
u64 str_ascii_number_to_ll( char* str )
{
int idx;
int idx2;
char number_copy[20];
idx = 0; // index into the main string we are searching
idx2 = 0; // index used for backup copy of ascii number
while( str[idx] != 0 )
{
/* Find the start of the number */
if( str[idx] >= '0' && str[idx] <= '9' )
{
while( str[idx] != 0 )
{
/* Find the end of the number */
if( str[idx] >= '0' && str[idx] <= '9' )
{
if( idx2 < sizeof( number_copy ) - 1 )
{
number_copy[idx2++] = str[idx++];
}
else
{
/* Number is too large ! */
return -1;
}
}
else
{
/* end found */
number_copy[idx2] = 0; // terminate our copy
/* convert ascii number to longlong */
return atoll( number_copy );
}
}
}
else
{
idx++;
}
}
return -2; /* no number found */
}

55
src/miscellaneous.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* miscellaneous.h: header file for miscellaneous.c ..
*
* functions that may be generally used throughout nwipes code,
* mainly string processing related functions.
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef MISCELLANEOUS_H_
#define MISCELLANEOUS_H_
/**
* Convert the string from lower to upper case
* @param pointer to a null terminated string
* @return void
*/
void strupper( char* );
/**
* Convert the string from upper to lower case
* @param pointer to a null terminated string
* @return void
*/
void strlower( char* str );
/**
* Search a string for a positive number, convert the first
* number found to binary and return the binary number.
* returns the number or -1 if number too large or -2 if
* no number found.
*
* @param pointer to a null terminated string
* @return longlong returns:
* the number
* -1 = number too large
* -2 = no number found.
*/
u64 str_ascii_number_to_ll( char* );
#endif /* HPA_DCO_H_ */

View File

@@ -4,7 +4,7 @@
* used by configure to dynamically assign those values
* to documentation files.
*/
const char* version_string = "0.34";
const char* version_string = "0.34.1 Development code, not for production use!";
const char* program_name = "nwipe";
const char* author_name = "Martijn van Brummelen";
const char* email_address = "git@brumit.nl";
@@ -14,4 +14,4 @@ Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>\n\
This is free software; see the source for copying conditions.\n\
There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\
FOR A PARTICULAR PURPOSE.\n";
const char* banner = "nwipe 0.34";
const char* banner = "nwipe 0.34.1 Development code, not for production use!";