+ libimobiledevice-1.2.0-12

Replace patches with a single mega-patch
Fixes usbmuxd for iOS 11 devices
This commit is contained in:
Bastien Nocera 2017-09-15 14:24:12 +02:00
parent 5f89cc6a0c
commit ca0c86589d
9 changed files with 5326 additions and 424 deletions

View File

@ -1,62 +0,0 @@
From 692f7c9de72ca7fcaba51659972270d445751438 Mon Sep 17 00:00:00 2001
From: BALATON Zoltan <balaton@eik.bme.hu>
Date: Wed, 23 Sep 2015 02:19:27 +0200
Subject: [PATCH] Add new function to get the underlying file descriptor of an
idevice connection
---
include/libimobiledevice/libimobiledevice.h | 10 ++++++++++
src/idevice.c | 16 ++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/include/libimobiledevice/libimobiledevice.h b/include/libimobiledevice/libimobiledevice.h
index 016cadb..b125adf 100644
--- a/include/libimobiledevice/libimobiledevice.h
+++ b/include/libimobiledevice/libimobiledevice.h
@@ -239,6 +239,16 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection);
*/
idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection);
+/**
+ * Get the underlying file descriptor for a connection
+ *
+ * @param connection The connection to get fd of
+ * @param fd Pointer to an int where the fd is stored
+ *
+ * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
+ */
+idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd);
+
/* misc */
/**
diff --git a/src/idevice.c b/src/idevice.c
index b776e84..5912aeb 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -463,6 +463,22 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive(idevice_connecti
return internal_connection_receive(connection, data, len, recv_bytes);
}
+LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd)
+{
+ if (!connection || !fd) {
+ return IDEVICE_E_INVALID_ARG;
+ }
+
+ idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR;
+ if (connection->type == CONNECTION_USBMUXD) {
+ *fd = (int)(long)connection->data;
+ result = IDEVICE_E_SUCCESS;
+ } else {
+ debug_info("Unknown connection type %d", connection->type);
+ }
+ return result;
+}
+
LIBIMOBILEDEVICE_API idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle)
{
if (!device)
--
2.9.3

View File

@ -1,28 +0,0 @@
From 13bf235cac2201747de11652cf14fe2714ca0718 Mon Sep 17 00:00:00 2001
From: David Weinstein <dweinst@insitusec.com>
Date: Mon, 21 Mar 2016 17:45:59 -0400
Subject: [PATCH] Fix SSL version negotiation for newer versions of OpenSSL
Depending on the OpenSSL version (and custom distribution patches), `SSLv3_method()`
would return NULL on some systems and also `SSLv23_method()` fails with some older
iOS versions...
---
src/idevice.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idevice.c b/src/idevice.c
index f2de6a3..1dcdae2 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -703,7 +703,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne
}
BIO_set_fd(ssl_bio, (int)(long)connection->data, BIO_NOCLOSE);
- SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv3_method());
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_method());
if (ssl_ctx == NULL) {
debug_info("ERROR: Could not create SSL context.");
BIO_free(ssl_bio);
--
2.9.3

View File

@ -1,41 +0,0 @@
From 6070126868069f2ee01ea9414f4cfbe5de285267 Mon Sep 17 00:00:00 2001
From: "Jay Freeman (saurik)" <saurik@saurik.com>
Date: Wed, 21 Oct 2015 00:39:14 -0700
Subject: [PATCH] Fix installation_proxy when using GnuTLS instead of OpenSSL
---
src/idevice.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/idevice.c b/src/idevice.c
index 7c33cdd..b776e84 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -393,10 +393,13 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_
}
if (connection->ssl_data) {
-#ifdef HAVE_OPENSSL
uint32_t received = 0;
while (received < len) {
+#ifdef HAVE_OPENSSL
int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received);
+#else
+ ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received);
+#endif
if (r > 0) {
received += r;
} else {
@@ -404,9 +407,6 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_
}
}
debug_info("SSL_read %d, received %d", len, received);
-#else
- ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len);
-#endif
if (received > 0) {
*recv_bytes = received;
return IDEVICE_E_SUCCESS;
--
2.5.0

View File

@ -1,54 +0,0 @@
From 2a5868411c57e25802d2f16fd6b77601f10d0b72 Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@redhat.com>
Date: Fri, 29 Apr 2016 22:58:34 +0200
Subject: [PATCH] Updated gnutls certificate callback to new API (backwards
compatible)
---
src/idevice.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/idevice.c b/src/idevice.c
index 5912aeb..f2de6a3 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -642,7 +642,11 @@ static const char *ssl_error_to_string(int e)
/**
* Internally used gnutls callback function that gets called during handshake.
*/
+#if GNUTLS_VERSION_NUMBER >= 0x020b07
+static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st)
+#else
static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st)
+#endif
{
int res = -1;
gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
@@ -650,7 +654,12 @@ static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t
ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session);
if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) {
debug_info("Passing certificate");
+#if GNUTLS_VERSION_NUMBER >= 0x020b07
+ st->cert_type = type;
+ st->key_type = GNUTLS_PRIVKEY_X509;
+#else
st->type = type;
+#endif
st->ncerts = 1;
st->cert.x509 = &ssl_data->host_cert;
st->key.x509 = ssl_data->host_privkey;
@@ -759,7 +768,11 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne
debug_info("enabling SSL mode");
errno = 0;
gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate);
+#if GNUTLS_VERSION_NUMBER >= 0x020b07
+ gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
+#else
gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
+#endif
gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT);
gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-SSL3.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL);
gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate);
--
2.9.3

View File

@ -1,29 +0,0 @@
From 72643b2b83990b9cf97cc84b285b30763d44a72d Mon Sep 17 00:00:00 2001
From: "Jay Freeman (saurik)" <saurik@saurik.com>
Date: Tue, 2 Aug 2016 03:08:04 -0700
Subject: [PATCH] idevice: Update GnuTLS code to support iOS 10
As of iOS 10 beta 4, the GnuTLS implementation idevice_connection_enable_ssl
needs to be updated to support TLS. Using +VERS-TLS-ALL did not work on some
of the devices I tested and I wasn't sure how to fix it, but +VERS-TLS1.0 is
working on every device I've tested: iOS 9.0.2, 10.0b4, 8.1.1, 6.0, and 3.0.
---
src/idevice.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idevice.c b/src/idevice.c
index 1dcdae2..b6dfe4e 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -774,7 +774,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne
gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
#endif
gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT);
- gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-SSL3.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL);
+ gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL);
gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate);
gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc);
--
2.9.3

View File

@ -1,171 +0,0 @@
From 23069d10341ce637fdad7321d447c53752dba48c Mon Sep 17 00:00:00 2001
From: Nikias Bassen <nikias@gmx.li>
Date: Fri, 4 Nov 2016 02:11:39 +0100
Subject: [PATCH] userpref: [GnuTLS] Fix pairing record generation and improve
error handling
In newer GnuTLS versions the parameters supplied to
gnutls_x509_privkey_import_rsa_raw() are actually checked for somewhat
sane values. Since we were passing the same values for all parameters,
this check fails and the device certificate is never generated.
However due to missing checks the pairing record was saved anyway, with
an empty device certificate. This led to TLS errors during communication,
leading to the "GnuTLS: Error in pull function" error message appearing
and the communication to fail.
This commit fixes the issue by passing some sane values, and also improves
the overall error handling during generation of the paring record.
---
common/userpref.c | 85 +++++++++++++++++++++++++++++--------------------------
1 file changed, 45 insertions(+), 40 deletions(-)
diff --git a/common/userpref.c b/common/userpref.c
index d22c7f5..3ae503a 100644
--- a/common/userpref.c
+++ b/common/userpref.c
@@ -643,15 +643,13 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_export_size);
host_cert_pem.size = host_cert_export_size;
- ret = USERPREF_E_UNKNOWN_ERROR;
-
gnutls_datum_t modulus = { NULL, 0 };
gnutls_datum_t exponent = { NULL, 0 };
/* now decode the PEM encoded key */
- gnutls_datum_t der_pub_key;
- if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) {
-
+ gnutls_datum_t der_pub_key = { NULL, 0 };
+ int gnutls_error = gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key);
+ if (GNUTLS_E_SUCCESS == gnutls_error) {
/* initalize asn.1 parser */
ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY;
if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) {
@@ -670,8 +668,14 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size);
ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size);
- if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2)
- ret = USERPREF_E_SUCCESS;
+ if (ret1 != ASN1_SUCCESS || ret2 != ASN1_SUCCESS) {
+ gnutls_free(modulus.data);
+ modulus.data = NULL;
+ modulus.size = 0;
+ gnutls_free(exponent.data);
+ exponent.data = NULL;
+ exponent.size = 0;
+ }
}
if (asn1_pub_key)
asn1_delete_structure(&asn1_pub_key);
@@ -679,12 +683,15 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
if (pkcs1)
asn1_delete_structure(&pkcs1);
} else {
- debug_info("WARNING: Could not read public key");
+ debug_info("ERROR: Could not parse public key: %s", gnutls_strerror(gnutls_error));
}
- /* now generate certificates */
- if (USERPREF_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
- gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") };
+ /* generate device certificate */
+ if (modulus.data && 0 != modulus.size && exponent.data && 0 != exponent.size) {
+
+ gnutls_datum_t prime_p = { (unsigned char*)"\x00\xca\x4a\x03\x13\xdf\x9d\x7a\xfd", 9 };
+ gnutls_datum_t prime_q = { (unsigned char*)"\x00\xf2\xff\xe0\x15\xd1\x60\x37\x63", 9 };
+ gnutls_datum_t coeff = { (unsigned char*)"\x32\x07\xf1\x68\x57\xdf\x9a\xf4", 8 };
gnutls_x509_privkey_t fake_privkey;
gnutls_x509_crt_t dev_cert;
@@ -692,8 +699,9 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_privkey_init(&fake_privkey);
gnutls_x509_crt_init(&dev_cert);
- if (GNUTLS_E_SUCCESS == gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, &essentially_null, &essentially_null)) {
- /* generate device certificate */
+ gnutls_error = gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &exponent, &prime_p, &prime_q, &coeff);
+ if (GNUTLS_E_SUCCESS == gnutls_error) {
+ /* now generate device certificate */
gnutls_x509_crt_set_key(dev_cert, fake_privkey);
gnutls_x509_crt_set_serial(dev_cert, "\x00", 1);
gnutls_x509_crt_set_version(dev_cert, 3);
@@ -712,9 +720,8 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
}
gnutls_x509_crt_set_key_usage(dev_cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT);
- gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
-
- if (USERPREF_E_SUCCESS == ret) {
+ gnutls_error = gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
+ if (GNUTLS_E_SUCCESS == gnutls_error) {
/* if everything went well, export in PEM format */
size_t export_size = 0;
gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size);
@@ -722,13 +729,11 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_cert_pem.data, &export_size);
dev_cert_pem.size = export_size;
} else {
- debug_info("ERROR: Signing device certificate with root private key failed!");
+ debug_info("ERROR: Signing device certificate with root private key failed: %s", gnutls_strerror(gnutls_error));
}
+ } else {
+ debug_info("ERROR: Failed to import RSA key data: %s", gnutls_strerror(gnutls_error));
}
-
- if (essentially_null.data)
- free(essentially_null.data);
-
gnutls_x509_crt_deinit(dev_cert);
gnutls_x509_privkey_deinit(fake_privkey);
}
@@ -743,27 +748,27 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_free(der_pub_key.data);
#endif
- if (NULL != root_cert_pem.data && 0 != root_cert_pem.size &&
- NULL != host_cert_pem.data && 0 != host_cert_pem.size)
+
+ /* make sure that we have all we need */
+ if (root_cert_pem.data && 0 != root_cert_pem.size
+ && root_key_pem.data && 0 != root_key_pem.size
+ && host_cert_pem.data && 0 != host_cert_pem.size
+ && host_key_pem.data && 0 != host_key_pem.size
+ && dev_cert_pem.data && 0 != dev_cert_pem.size) {
+ /* now set keys and certificates */
+ pair_record_set_item_from_key_data(pair_record, USERPREF_DEVICE_CERTIFICATE_KEY, &dev_cert_pem);
+ pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, &host_key_pem);
+ pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_CERTIFICATE_KEY, &host_cert_pem);
+ pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_key_pem);
+ pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert_pem);
ret = USERPREF_E_SUCCESS;
+ }
- /* now set keys and certificates */
- pair_record_set_item_from_key_data(pair_record, USERPREF_DEVICE_CERTIFICATE_KEY, &dev_cert_pem);
- pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, &host_key_pem);
- pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_CERTIFICATE_KEY, &host_cert_pem);
- pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_key_pem);
- pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert_pem);
-
- if (dev_cert_pem.data)
- free(dev_cert_pem.data);
- if (root_key_pem.data)
- free(root_key_pem.data);
- if (root_cert_pem.data)
- free(root_cert_pem.data);
- if (host_key_pem.data)
- free(host_key_pem.data);
- if (host_cert_pem.data)
- free(host_cert_pem.data);
+ free(dev_cert_pem.data);
+ free(root_key_pem.data);
+ free(root_cert_pem.data);
+ free(host_key_pem.data);
+ free(host_cert_pem.data);
return ret;
}
--
2.9.3

View File

@ -1,31 +0,0 @@
From df1f5c4d70d0c19ad40072f5246ca457e7f9849e Mon Sep 17 00:00:00 2001
From: Joshua Hill <posixninja@gmail.com>
Date: Tue, 29 Dec 2015 22:27:17 +0100
Subject: [PATCH] common: [security fix] Make sure sockets only listen locally
---
common/socket.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/socket.c b/common/socket.c
index b276864..e2968a6 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -172,7 +172,7 @@ int socket_create(uint16_t port)
memset((void *) &saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
saddr.sin_port = htons(port);
if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
@@ -329,7 +329,7 @@ int socket_accept(int fd, uint16_t port)
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(port);
addr_len = sizeof(addr);

View File

@ -7,20 +7,17 @@
Name: libimobiledevice
Version: 1.2.0
Release: 11%{?dist}
Release: 12%{?dist}
Summary: Library for connecting to mobile devices
Group: System Environment/Libraries
License: LGPLv2+
URL: http://www.libimobiledevice.org/
Source0: http://www.libimobiledevice.org/downloads/%{name}-%{version}.tar.bz2
Patch1: 0001-Fix-installation_proxy-when-using-GnuTLS-instead-of-.patch
Patch2: CVE-2016-5104.patch
Patch3: 0001-Add-new-function-to-get-the-underlying-file-descript.patch
Patch4: 0001-Updated-gnutls-certificate-callback-to-new-API-backw.patch
Patch5: 0001-Fix-SSL-version-negotiation-for-newer-versions-of-Op.patch
Patch6: 0001-idevice-Update-GnuTLS-code-to-support-iOS-10.patch
Patch7: 0001-userpref-GnuTLS-Fix-pairing-record-generation-and-im.patch
# Upstream patches, generated with:
# git format-patch --stdout 344409e1d1ad917d377b256214c5411dda82e6b0...5a85432719fb3d18027d528f87d2a44b76fd3e12
# b5a70e9aaf538dad0aba0b800b122955e8ac494b was manually removed
Patch0: 344409e1d1ad917d377b256214c5411dda82e6b0...5a85432719fb3d18027d528f87d2a44b76fd3e12.patch
BuildRequires: glib2-devel
BuildRequires: gnutls-devel
@ -38,6 +35,7 @@ BuildRequires: readline-devel
BuildRequires: Cython
%endif
BuildRequires: swig
BuildRequires: autoconf automake libtool
%description
libimobiledevice is a library for connecting to mobile devices including phones
@ -73,6 +71,8 @@ Python bindings for libimobiledevice.
# Fix dir permissions on html docs
chmod +x docs/html
autoreconf -f
%build
%configure --disable-static --disable-openssl --enable-dev-tools %{!?python:--without-cython}
# Remove rpath as per https://fedoraproject.org/wiki/Packaging/Guidelines#Beware_of_Rpath
@ -115,6 +115,11 @@ find %{buildroot} -type f -name "*.la" -delete
%endif
%changelog
* Fri Sep 15 2017 Bastien Nocera <bnocera@redhat.com> - 1.2.0-12
+ libimobiledevice-1.2.0-12
- Replace patches with a single mega-patch
- Fixes usbmuxd for iOS 11 devices
* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.2.0-11
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild