Obtain AIK and exchange as PEM certificate done
Fixed the trashy tail of last file name in Request File Measurement
This commit is contained in:
committed by
Andreas Steffen
parent
90c963439e
commit
f4527909f2
@@ -282,7 +282,19 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
|
|||||||
|
|
||||||
case TCG_PTS_GET_AIK:
|
case TCG_PTS_GET_AIK:
|
||||||
{
|
{
|
||||||
/* TODO: Implement AIK retrieve */
|
chunk_t aik;
|
||||||
|
bool is_naked_key;
|
||||||
|
|
||||||
|
if (!pts->get_aik(pts, &aik, &is_naked_key))
|
||||||
|
{
|
||||||
|
DBG1(DBG_IMC,"Obtaining AIK Certificate failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send AIK attribute */
|
||||||
|
attr_to_send = tcg_pts_attr_aik_create(is_naked_key, aik);
|
||||||
|
attr_to_send = (pa_tnc_attr_t*)attr_to_send;
|
||||||
|
attr_list->insert_last(attr_list,attr_to_send);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +321,10 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
|
|||||||
request_id = attr_cast->get_request_id(attr_cast);
|
request_id = attr_cast->get_request_id(attr_cast);
|
||||||
delimiter = attr_cast->get_delimiter(attr_cast);
|
delimiter = attr_cast->get_delimiter(attr_cast);
|
||||||
path = attr_cast->get_file_path(attr_cast);
|
path = attr_cast->get_file_path(attr_cast);
|
||||||
|
path = chunk_clone(path);
|
||||||
|
|
||||||
DBG3(DBG_IMC,"requested %s to be measured: %s",
|
DBG3(DBG_IMC,"requested %s to be measured: %B",
|
||||||
(directory_flag)? "directory":"file", path.ptr);
|
(directory_flag)? "directory":"file", &path);
|
||||||
|
|
||||||
/* Send File Measurement attribute */
|
/* Send File Measurement attribute */
|
||||||
selected_algorithm = pts->get_meas_algorithm(pts);
|
selected_algorithm = pts->get_meas_algorithm(pts);
|
||||||
@@ -342,8 +355,7 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
|
|||||||
DBG1(DBG_IMC, "Hashing the given file has failed");
|
DBG1(DBG_IMC, "Hashing the given file has failed");
|
||||||
return TNC_RESULT_FATAL;
|
return TNC_RESULT_FATAL;
|
||||||
}
|
}
|
||||||
attr_file_meas->add_file_meas(attr_file_meas, file_hash, chunk_clone(path));
|
attr_file_meas->add_file_meas(attr_file_meas, file_hash, path);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -221,15 +221,15 @@ static TNC_Result send_message(TNC_ConnectionID connection_id)
|
|||||||
{
|
{
|
||||||
pa_tnc_attr_t *attr_get_tpm_version, *attr_get_aik;
|
pa_tnc_attr_t *attr_get_tpm_version, *attr_get_aik;
|
||||||
|
|
||||||
|
/* Send Get TPM Version attribute */
|
||||||
attr_get_tpm_version = tcg_pts_attr_get_tpm_version_info_create();
|
attr_get_tpm_version = tcg_pts_attr_get_tpm_version_info_create();
|
||||||
attr_get_tpm_version->set_noskip_flag(attr_get_tpm_version, TRUE);
|
attr_get_tpm_version->set_noskip_flag(attr_get_tpm_version, TRUE);
|
||||||
msg->add_attribute(msg, attr_get_tpm_version);
|
msg->add_attribute(msg, attr_get_tpm_version);
|
||||||
|
|
||||||
/* Send Get AIK attribute */
|
/* Send Get AIK attribute */
|
||||||
/* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
|
attr_get_aik = tcg_pts_attr_get_aik_create();
|
||||||
//attr_get_aik = tcg_pts_attr_get_aik_create();
|
attr_get_aik->set_noskip_flag(attr_get_aik, TRUE);
|
||||||
//attr_get_aik->set_noskip_flag(attr_get_aik, TRUE);
|
msg->add_attribute(msg, attr_get_aik);
|
||||||
//msg->add_attribute(msg, attr_get_aik);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send Request File Measurement attribute */
|
/* Send Request File Measurement attribute */
|
||||||
@@ -255,6 +255,7 @@ static TNC_Result send_message(TNC_ConnectionID connection_id)
|
|||||||
DBG2(DBG_IMV, "id = %d, type = %d, path = '%s'", id, type, path);
|
DBG2(DBG_IMV, "id = %d, type = %d, path = '%s'", id, type, path);
|
||||||
|
|
||||||
is_directory = (type != 0) ? true : false;
|
is_directory = (type != 0) ? true : false;
|
||||||
|
path[strlen(path)] = '\0';
|
||||||
path_chunk = chunk_create(path, strlen(path));
|
path_chunk = chunk_create(path, strlen(path));
|
||||||
path_chunk = chunk_clone(path_chunk);
|
path_chunk = chunk_clone(path_chunk);
|
||||||
|
|
||||||
@@ -406,7 +407,15 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
|
|||||||
}
|
}
|
||||||
case TCG_PTS_AIK:
|
case TCG_PTS_AIK:
|
||||||
{
|
{
|
||||||
/* TODO: Save the AIK key and certificate */
|
tcg_pts_attr_aik_t *attr_cast;
|
||||||
|
chunk_t aik;
|
||||||
|
bool is_naked_key;
|
||||||
|
|
||||||
|
attr_cast = (tcg_pts_attr_aik_t*)attr;
|
||||||
|
aik = attr_cast->get_aik(attr_cast);
|
||||||
|
is_naked_key = attr_cast->get_naked_flag(attr_cast);
|
||||||
|
pts->set_aik(pts, aik, is_naked_key);
|
||||||
|
|
||||||
attestation_state->set_handshake_state(attestation_state,
|
attestation_state->set_handshake_state(attestation_state,
|
||||||
IMV_ATTESTATION_STATE_END);
|
IMV_ATTESTATION_STATE_END);
|
||||||
break;
|
break;
|
||||||
@@ -457,7 +466,7 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
|
|||||||
DBG3(DBG_IMV, "Expected measurement: %B", &db_measurement);
|
DBG3(DBG_IMV, "Expected measurement: %B", &db_measurement);
|
||||||
|
|
||||||
/* Compare the received hash measurement with one saved in db */
|
/* Compare the received hash measurement with one saved in db */
|
||||||
if(chunk_compare(db_measurement, meas_entry->measurement) == 0)
|
if(chunk_equals(db_measurement, meas_entry->measurement))
|
||||||
{
|
{
|
||||||
DBG1(DBG_IMV, "Measurement comparison succeeded for: %s", meas_entry->file_name.ptr);
|
DBG1(DBG_IMV, "Measurement comparison succeeded for: %s", meas_entry->file_name.ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-51
@@ -41,7 +41,7 @@
|
|||||||
#define REQURL CAURL "api/pca/level%d?ResponseFormat=Binary"
|
#define REQURL CAURL "api/pca/level%d?ResponseFormat=Binary"
|
||||||
|
|
||||||
/* TPM has EK Certificate */
|
/* TPM has EK Certificate */
|
||||||
/* #define REALEK */
|
#define REALEK FALSE
|
||||||
|
|
||||||
typedef struct private_pts_t private_pts_t;
|
typedef struct private_pts_t private_pts_t;
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ METHOD(pts_t, set_tpm_version_info, void,
|
|||||||
* Create a fake endorsement key cert using system's actual EK
|
* Create a fake endorsement key cert using system's actual EK
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool makeEKCert(TSS_HCONTEXT hContext, TSS_HTPM hTPM, UINT32 *pCertLen, BYTE **pCert)
|
static TSS_RESULT makeEKCert(TSS_HCONTEXT hContext, TSS_HTPM hTPM, UINT32 *pCertLen, BYTE **pCert)
|
||||||
{
|
{
|
||||||
TSS_RESULT result;
|
TSS_RESULT result;
|
||||||
TSS_HKEY hPubek;
|
TSS_HKEY hPubek;
|
||||||
@@ -186,18 +186,21 @@ static bool makeEKCert(TSS_HCONTEXT hContext, TSS_HTPM hTPM, UINT32 *pCertLen, B
|
|||||||
result = Tspi_TPM_GetPubEndorsementKey (hTPM, TRUE, NULL, &hPubek);
|
result = Tspi_TPM_GetPubEndorsementKey (hTPM, TRUE, NULL, &hPubek);
|
||||||
if (result != TSS_SUCCESS)
|
if (result != TSS_SUCCESS)
|
||||||
{
|
{
|
||||||
goto err;
|
DBG1(DBG_IMC, "Error in: Tspi_TPM_GetPubEndorsementKey\n");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
result = Tspi_GetAttribData (hPubek, TSS_TSPATTRIB_RSAKEY_INFO,
|
result = Tspi_GetAttribData (hPubek, TSS_TSPATTRIB_RSAKEY_INFO,
|
||||||
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modulusLen, &modulus);
|
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modulusLen, &modulus);
|
||||||
Tspi_Context_CloseObject (hContext, hPubek);
|
Tspi_Context_CloseObject (hContext, hPubek);
|
||||||
if (result != TSS_SUCCESS)
|
if (result != TSS_SUCCESS)
|
||||||
{
|
{
|
||||||
goto err;
|
DBG1(DBG_IMC, "Error in: Tspi_Context_CloseObject\n");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (modulusLen != 256) {
|
if (modulusLen != 256) {
|
||||||
|
DBG1(DBG_IMC, "Tspi_GetAttribData modulusLen != 256\n");
|
||||||
Tspi_Context_FreeMemory (hContext, modulus);
|
Tspi_Context_FreeMemory (hContext, modulus);
|
||||||
goto err;
|
return result;
|
||||||
}
|
}
|
||||||
*pCertLen = sizeof(fakeEKCert);
|
*pCertLen = sizeof(fakeEKCert);
|
||||||
*pCert = malloc (*pCertLen);
|
*pCert = malloc (*pCertLen);
|
||||||
@@ -205,11 +208,7 @@ static bool makeEKCert(TSS_HCONTEXT hContext, TSS_HTPM hTPM, UINT32 *pCertLen, B
|
|||||||
memcpy (*pCert + 0xc6, modulus, modulusLen);
|
memcpy (*pCert + 0xc6, modulus, modulusLen);
|
||||||
Tspi_Context_FreeMemory (hContext, modulus);
|
Tspi_Context_FreeMemory (hContext, modulus);
|
||||||
|
|
||||||
return TRUE;
|
return TSS_SUCCESS;
|
||||||
|
|
||||||
err:
|
|
||||||
DBG1(DBG_TNC, "TPM not available: tss error 0x%x", result);
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,7 +225,7 @@ static X509* readPCAcert (int level)
|
|||||||
int result;
|
int result;
|
||||||
|
|
||||||
hCurl = curl_easy_init ();
|
hCurl = curl_easy_init ();
|
||||||
DBG1(DBG_IMC, url, CERTURL, level);
|
sprintf (url, CERTURL, level);
|
||||||
curl_easy_setopt (hCurl, CURLOPT_URL, url);
|
curl_easy_setopt (hCurl, CURLOPT_URL, url);
|
||||||
curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (BYTE **)f_tmp);
|
curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (BYTE **)f_tmp);
|
||||||
|
|
||||||
@@ -248,7 +247,7 @@ static X509* readPCAcert (int level)
|
|||||||
* Obtain an AIK, SRK and TPM Owner secret has to be both set to well known secret
|
* Obtain an AIK, SRK and TPM Owner secret has to be both set to well known secret
|
||||||
* of 20 bytes of zero
|
* of 20 bytes of zero
|
||||||
*/
|
*/
|
||||||
static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
static bool obtain_aik(private_pts_t *this)
|
||||||
{
|
{
|
||||||
TSS_HCONTEXT hContext;
|
TSS_HCONTEXT hContext;
|
||||||
TSS_HTPM hTPM;
|
TSS_HTPM hTPM;
|
||||||
@@ -278,24 +277,24 @@ static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
|||||||
UINT32 asymBufSize;
|
UINT32 asymBufSize;
|
||||||
UINT32 symBufSize;
|
UINT32 symBufSize;
|
||||||
UINT32 credBufSize;
|
UINT32 credBufSize;
|
||||||
BYTE *blob;
|
static int level = 0;
|
||||||
UINT32 blobLen;
|
|
||||||
#ifdef REALEK
|
|
||||||
const int level = 1;
|
|
||||||
#else
|
|
||||||
const int level = 0;
|
|
||||||
BYTE *ekCert = NULL;
|
BYTE *ekCert = NULL;
|
||||||
UINT32 ekCertLen;
|
UINT32 ekCertLen;
|
||||||
#endif
|
|
||||||
char url[128];
|
char url[128];
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
*aik = chunk_empty;
|
this->aik = chunk_empty;
|
||||||
*is_naked_key = false;
|
this->is_naked_key = false;
|
||||||
|
|
||||||
curl_global_init (CURL_GLOBAL_ALL);
|
curl_global_init (CURL_GLOBAL_ALL);
|
||||||
|
|
||||||
DBG3(DBG_IMC, "Retrieving PCA certificate...\n");
|
DBG3(DBG_IMC, "Retrieving PCA certificate...\n");
|
||||||
|
|
||||||
|
/* TPM has EK Certificate */
|
||||||
|
if(REALEK)
|
||||||
|
{
|
||||||
|
level = 1;
|
||||||
|
}
|
||||||
x509 = readPCAcert (level);
|
x509 = readPCAcert (level);
|
||||||
if (x509 == NULL) {
|
if (x509 == NULL) {
|
||||||
DBG1(DBG_IMC, "Error reading PCA key\n");
|
DBG1(DBG_IMC, "Error reading PCA key\n");
|
||||||
@@ -385,20 +384,21 @@ static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef REALEK
|
if(!REALEK)
|
||||||
result = makeEKCert(hContext, hTPM, &ekCertLen, &ekCert);
|
{
|
||||||
if (result != TSS_SUCCESS) {
|
result = makeEKCert(hContext, hTPM, &ekCertLen, &ekCert);
|
||||||
DBG1(DBG_IMC, "Error 0x%x on makeEKCert\n", result);
|
if (result != TSS_SUCCESS) {
|
||||||
goto err;
|
DBG1(DBG_IMC, "Error 0x%x on makeEKCert\n", result);
|
||||||
}
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
result = Tspi_SetAttribData(hTPM, TSS_TSPATTRIB_TPM_CREDENTIAL,
|
result = Tspi_SetAttribData(hTPM, TSS_TSPATTRIB_TPM_CREDENTIAL,
|
||||||
TSS_TPMATTRIB_EKCERT, ekCertLen, ekCert);
|
TSS_TPMATTRIB_EKCERT, ekCertLen, ekCert);
|
||||||
if (result != TSS_SUCCESS) {
|
if (result != TSS_SUCCESS) {
|
||||||
DBG1(DBG_IMC, "Error 0x%x on SetAttribData for EKCert\n", result);
|
DBG1(DBG_IMC, "Error 0x%x on SetAttribData for EKCert\n", result);
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
DBG3(DBG_IMC, "Generating attestation identity key...\n");
|
DBG3(DBG_IMC, "Generating attestation identity key...\n");
|
||||||
result = Tspi_TPM_CollateIdentityRequest(hTPM, hSRK, hPCAKey, 0,
|
result = Tspi_TPM_CollateIdentityRequest(hTPM, hSRK, hPCAKey, 0,
|
||||||
@@ -415,7 +415,7 @@ static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
|||||||
/* Send to server */
|
/* Send to server */
|
||||||
f_tmp = tmpfile();
|
f_tmp = tmpfile();
|
||||||
hCurl = curl_easy_init ();
|
hCurl = curl_easy_init ();
|
||||||
DBG3(DBG_IMC, "URL: %s\nRequest URL: %s\n Certificate Level: %d", url, REQURL, level);
|
sprintf (url, REQURL, level);
|
||||||
curl_easy_setopt (hCurl, CURLOPT_URL, url);
|
curl_easy_setopt (hCurl, CURLOPT_URL, url);
|
||||||
curl_easy_setopt (hCurl, CURLOPT_POSTFIELDS, (void *)rgbTCPAIdentityReq);
|
curl_easy_setopt (hCurl, CURLOPT_POSTFIELDS, (void *)rgbTCPAIdentityReq);
|
||||||
curl_easy_setopt (hCurl, CURLOPT_POSTFIELDSIZE, ulTCPAIdentityReqLength);
|
curl_easy_setopt (hCurl, CURLOPT_POSTFIELDSIZE, ulTCPAIdentityReqLength);
|
||||||
@@ -469,18 +469,6 @@ static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Output key blob */
|
|
||||||
result = Tspi_GetAttribData (hIdentKey, TSS_TSPATTRIB_KEY_BLOB,
|
|
||||||
TSS_TSPATTRIB_KEYBLOB_BLOB, &blobLen, &blob);
|
|
||||||
if (result != TSS_SUCCESS) {
|
|
||||||
DBG1(DBG_IMC, "Error 0x%x on Tspi_GetAttribData for key blob\n", result);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Do something with blob variable */
|
|
||||||
|
|
||||||
Tspi_Context_FreeMemory (hContext, blob);
|
|
||||||
|
|
||||||
/* Output credential in PEM format */
|
/* Output credential in PEM format */
|
||||||
tbuf = credBuf;
|
tbuf = credBuf;
|
||||||
x509 = d2i_X509(NULL, (const BYTE **)&tbuf, credBufSize);
|
x509 = d2i_X509(NULL, (const BYTE **)&tbuf, credBufSize);
|
||||||
@@ -492,8 +480,33 @@ static bool obtain_aik(chunk_t *aik, bool *is_naked_key)
|
|||||||
DBG1(DBG_IMC, "Note, not all data from privacy ca was parsed correctly\n");
|
DBG1(DBG_IMC, "Note, not all data from privacy ca was parsed correctly\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Do something with X509 object */
|
if(x509)
|
||||||
X509_free (x509);
|
{
|
||||||
|
BUF_MEM *mem_buf;
|
||||||
|
BIO* bp;
|
||||||
|
u_int32_t len;
|
||||||
|
|
||||||
|
bp = BIO_new(BIO_s_mem());
|
||||||
|
PEM_write_bio_X509(bp, x509);
|
||||||
|
|
||||||
|
len = BIO_get_mem_data(bp, &mem_buf);
|
||||||
|
char tmp[len+1];
|
||||||
|
|
||||||
|
memcpy(tmp, mem_buf, len);
|
||||||
|
tmp[len] = '\0';
|
||||||
|
|
||||||
|
DBG3(DBG_IMC,"X509 Certificate (PEM format):\n%s\n", tmp);
|
||||||
|
this->aik = chunk_create(tmp, len + 1);
|
||||||
|
this->aik = chunk_clone(this->aik);
|
||||||
|
|
||||||
|
X509_free (x509);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG1(DBG_IMC, "Neither AIK Key blob, nor AIK Certificate is available\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
DBG3(DBG_IMC, "Succeeded at obtaining AIK Certificate from Privacy CA!\n");
|
DBG3(DBG_IMC, "Succeeded at obtaining AIK Certificate from Privacy CA!\n");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -505,7 +518,7 @@ err:
|
|||||||
METHOD(pts_t, get_aik, bool,
|
METHOD(pts_t, get_aik, bool,
|
||||||
private_pts_t *this, chunk_t *aik, bool *is_naked_key)
|
private_pts_t *this, chunk_t *aik, bool *is_naked_key)
|
||||||
{
|
{
|
||||||
if(obtain_aik(aik, is_naked_key) != TSS_SUCCESS )
|
if(obtain_aik(this) != TRUE )
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -527,6 +540,7 @@ METHOD(pts_t, hash_file, bool,
|
|||||||
private_pts_t *this, chunk_t path, chunk_t *out)
|
private_pts_t *this, chunk_t path, chunk_t *out)
|
||||||
{
|
{
|
||||||
char buffer[PTS_BUF_SIZE];
|
char buffer[PTS_BUF_SIZE];
|
||||||
|
chunk_t path_chunk;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
hasher_t *hasher;
|
hasher_t *hasher;
|
||||||
@@ -541,7 +555,8 @@ METHOD(pts_t, hash_file, bool,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = fopen(path.ptr, "rb");
|
path_chunk = chunk_create_clone(malloc(path.len), path);
|
||||||
|
file = fopen(path_chunk.ptr, "rb");
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
DBG1(DBG_IMC,"file '%s' can not be opened, %s", path.ptr, strerror(errno));
|
DBG1(DBG_IMC,"file '%s' can not be opened, %s", path.ptr, strerror(errno));
|
||||||
@@ -573,13 +588,15 @@ METHOD(pts_t, hash_directory, bool,
|
|||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
|
chunk_t path_chunk;
|
||||||
file_meas_entry_t *entry;
|
file_meas_entry_t *entry;
|
||||||
linked_list_t *list = *file_measurements;
|
linked_list_t *list = *file_measurements;
|
||||||
|
|
||||||
list = linked_list_create();
|
list = linked_list_create();
|
||||||
entry = malloc_thing(file_meas_entry_t);
|
entry = malloc_thing(file_meas_entry_t);
|
||||||
|
|
||||||
dir = opendir(path.ptr);
|
path_chunk = chunk_create_clone(malloc(path.len), path);
|
||||||
|
dir = opendir(path_chunk.ptr);
|
||||||
if (dir == NULL)
|
if (dir == NULL)
|
||||||
{
|
{
|
||||||
DBG1(DBG_IMC, "opening directory '%s' failed: %s", path.ptr, strerror(errno));
|
DBG1(DBG_IMC, "opening directory '%s' failed: %s", path.ptr, strerror(errno));
|
||||||
|
|||||||
Reference in New Issue
Block a user