From: Michihiro NAKAJIMA Date: Mon, 15 Sep 2014 09:10:58 +0000 (+0900) Subject: Property handle __archive_read_next_passphrase function. X-Git-Tag: v3.1.900a~212 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cf78910dda7ea14218a4d857e939a08450befd85;p=thirdparty%2Flibarchive.git Property handle __archive_read_next_passphrase function. Return the same passphrase while the passphraes is passed even if it was passed by a callback function. --- diff --git a/libarchive/archive_read_add_passphrase.c b/libarchive/archive_read_add_passphrase.c index da8ce5061..f67f1ebc6 100644 --- a/libarchive/archive_read_add_passphrase.c +++ b/libarchive/archive_read_add_passphrase.c @@ -51,6 +51,35 @@ remove_passphrases_from_head(struct archive_read *a) return (p); } +static void +insert_passphrase_to_head(struct archive_read *a, + struct archive_read_passphrase *p) +{ + p->next = a->passphrases.first; + a->passphrases.first = p; +} + +static struct archive_read_passphrase * +new_read_passphrase(struct archive_read *a, const char *passphrase) +{ + struct archive_read_passphrase *p; + + p = malloc(sizeof(*p)); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (NULL); + } + p->passphrase = strdup(passphrase); + if (p->passphrase == NULL) { + free(p); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (NULL); + } + return (p); +} + int archive_read_add_passphrase(struct archive *_a, const char *passphrase) { @@ -66,19 +95,9 @@ archive_read_add_passphrase(struct archive *_a, const char *passphrase) return (ARCHIVE_FAILED); } - p = malloc(sizeof(*p)); - if (p == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory"); + p = new_read_passphrase(a, passphrase); + if (p == NULL) return (ARCHIVE_FATAL); - } - p->passphrase = strdup(passphrase); - if (p->passphrase == NULL) { - free(p); - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory"); - return (ARCHIVE_FATAL); - } add_passphrase_to_tail(a, p); return (ARCHIVE_OK); @@ -153,6 +172,13 @@ __archive_read_next_passphrase(struct archive_read *a) * have it. */ passphrase = a->passphrases.callback(&a->archive, a->passphrases.client_data); + if (passphrase != NULL) { + p = new_read_passphrase(a, passphrase); + if (p == NULL) + return (NULL); + insert_passphrase_to_head(a, p); + a->passphrases.candiate = 1; + } } else passphrase = NULL; diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index a243c2116..7b28bff54 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -1573,21 +1573,13 @@ static int init_traditional_PKWARE_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); - const char *passphrase; const void *p; - uint8_t crcchk; + int retry; int r; if (zip->tctx_valid) return (ARCHIVE_OK); - passphrase = __archive_read_next_passphrase(a); - if (passphrase == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Passowrd required for this entry"); - return (ARCHIVE_FAILED); - } - /* Read the 12 bytes encryption header stored at the start of the data area. @@ -1599,15 +1591,33 @@ init_traditional_PKWARE_decryption(struct archive_read *a) "Truncated ZIP file data"); return (ARCHIVE_FATAL); } - /* - * Initialize ctx for Traditional PKWARE Decyption. - */ - r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), - p, ENC_HEADER_SIZE, &crcchk); - if (crcchk != zip->entry->decdat || r != 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Incorrect passowrd"); - return (ARCHIVE_FAILED); + + for (retry = 0;; retry++) { + const char *passphrase; + uint8_t crcchk; + + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + + /* + * Initialize ctx for Traditional PKWARE Decyption. + */ + r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), + p, ENC_HEADER_SIZE, &crcchk); + if (r == 0 && crcchk == zip->entry->decdat) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } } __archive_read_consume(a, ENC_HEADER_SIZE); @@ -1625,23 +1635,16 @@ static int init_WinZip_AES_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); - const char *passphrase; const void *p; const uint8_t *pv; size_t key_len, salt_len; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; + int retry; int r; if (zip->cctx_valid || zip->hctx_valid) return (ARCHIVE_OK); - passphrase = __archive_read_next_passphrase(a); - if (passphrase == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Passowrd required for this entry"); - return (ARCHIVE_FAILED); - } - switch (zip->entry->aes_extra.strength) { case 1: salt_len = 8; key_len = 16; break; case 2: salt_len = 12; key_len = 24; break; @@ -1652,17 +1655,38 @@ init_WinZip_AES_decryption(struct archive_read *a) if (p == NULL) goto truncated; - memset(derived_key, 0, sizeof(derived_key)); - archive_pbkdf2_sha1(passphrase, strlen(passphrase), - p, salt_len, 1000, derived_key, key_len * 2 + 2); + for (retry = 0;; retry++) { + const char *passphrase; - /* Check password verification value. */ - pv = ((const uint8_t *)p) + salt_len; - if (derived_key[key_len * 2] != pv[0] || - derived_key[key_len * 2 + 1] != pv[1]) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid passowrd"); - return (ARCHIVE_FAILED); + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + memset(derived_key, 0, sizeof(derived_key)); + r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), + p, salt_len, 1000, derived_key, key_len * 2 + 2); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Decryption is unsupported due to lack of " + "crypto library"); + return (ARCHIVE_FAILED); + } + + /* Check password verification value. */ + pv = ((const uint8_t *)p) + salt_len; + if (derived_key[key_len * 2] == pv[0] && + derived_key[key_len * 2 + 1] == pv[1]) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } } r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); diff --git a/libarchive/test/test_archive_read_add_passphrase.c b/libarchive/test/test_archive_read_add_passphrase.c index 6c2a4c011..2811ab2f8 100644 --- a/libarchive/test/test_archive_read_add_passphrase.c +++ b/libarchive/test/test_archive_read_add_passphrase.c @@ -177,6 +177,28 @@ DEFINE_TEST(test_archive_read_add_passphrase_set_callback2) archive_read_free(a); } +DEFINE_TEST(test_archive_read_add_passphrase_set_callback3) +{ + struct archive* a = archive_read_new(); + struct archive_read *ar = (struct archive_read *)a; + int client_data = 0; + + assertEqualInt(ARCHIVE_OK, + archive_read_set_passphrase_callback(a, &client_data, callback2)); + + __archive_read_reset_passphrase(ar); + /* Fist call, we should get "passCallBack" as a passphrase. */ + assertEqualString("passCallBack", __archive_read_next_passphrase(ar)); + __archive_read_reset_passphrase(ar); + /* After reset passphrase, we should get "passCallBack"passphrase. */ + assertEqualString("passCallBack", __archive_read_next_passphrase(ar)); + /* Second call, we should get NULL which means all the pssphrases + * are passed already. */ + assertEqualString(NULL, __archive_read_next_passphrase(ar)); + + archive_read_free(a); +} + DEFINE_TEST(test_archive_read_add_passphrase_multiple_with_callback) { struct archive* a = archive_read_new(); @@ -202,3 +224,36 @@ DEFINE_TEST(test_archive_read_add_passphrase_multiple_with_callback) archive_read_free(a); } +DEFINE_TEST(test_archive_read_add_passphrase_multiple_with_callback2) +{ + struct archive* a = archive_read_new(); + struct archive_read *ar = (struct archive_read *)a; + int client_data = 0; + + assertEqualInt(ARCHIVE_OK, archive_read_add_passphrase(a, "pass1")); + assertEqualInt(ARCHIVE_OK, archive_read_add_passphrase(a, "pass2")); + assertEqualInt(ARCHIVE_OK, + archive_read_set_passphrase_callback(a, &client_data, callback2)); + + __archive_read_reset_passphrase(ar); + /* Fist call, we should get "pass1" as a passphrase. */ + assertEqualString("pass1", __archive_read_next_passphrase(ar)); + /* Second call, we should get "pass2" as a passphrase. */ + assertEqualString("pass2", __archive_read_next_passphrase(ar)); + /* Third call, we should get "passCallBack" as a passphrase. */ + assertEqualString("passCallBack", __archive_read_next_passphrase(ar)); + + __archive_read_reset_passphrase(ar); + /* After reset passphrase, we should get "passCallBack" passphrase. */ + assertEqualString("passCallBack", __archive_read_next_passphrase(ar)); + /* Second call, we should get "pass1" as a passphrase. */ + assertEqualString("pass1", __archive_read_next_passphrase(ar)); + /* Third call, we should get "passCallBack" as a passphrase. */ + assertEqualString("pass2", __archive_read_next_passphrase(ar)); + /* Fourth call, we should get NULL which means all the pssphrases + * are passed already. */ + assertEqualString(NULL, __archive_read_next_passphrase(ar)); + + archive_read_free(a); +} + diff --git a/libarchive/test/test_read_format_zip_traditional_encryption_data.c b/libarchive/test/test_read_format_zip_traditional_encryption_data.c index 3b9fc1102..531dcaaac 100644 --- a/libarchive/test/test_read_format_zip_traditional_encryption_data.c +++ b/libarchive/test/test_read_format_zip_traditional_encryption_data.c @@ -103,6 +103,11 @@ DEFINE_TEST(test_read_format_zip_traditional_encryption_data) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + /* Pass three passphrases to decrypt a file content. */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_add_passphrase(a, "invalid_pass")); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_add_passphrase(a, "invalid_phrase")); assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, "12345678")); assertEqualIntA(a, ARCHIVE_OK, diff --git a/libarchive/test/test_read_format_zip_winzip_aes.c b/libarchive/test/test_read_format_zip_winzip_aes.c index 1cd05c25a..3f8561310 100644 --- a/libarchive/test/test_read_format_zip_winzip_aes.c +++ b/libarchive/test/test_read_format_zip_winzip_aes.c @@ -89,6 +89,11 @@ test_winzip_aes(const char *refname) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + /* Pass three passphrases to decrypt a file content. */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_add_passphrase(a, "invalid_pass")); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_add_passphrase(a, "invalid_phrase")); assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, "password")); assertEqualIntA(a, ARCHIVE_OK,