Merge pull request #441 from PartialVolume/HPA_DCO_001

HPA_DCO_001 - Add HPA, DCO capability
This commit is contained in:
PartialVolume
2023-02-26 23:23:22 +00:00
committed by GitHub
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!";