summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Doliner <mark@kingant.net>2014-10-16 16:01:07 -0700
committerMark Doliner <mark@kingant.net>2014-10-16 16:01:07 -0700
commit07b6a1878addfce61990c5f63e4535923ad323f7 (patch)
tree4c16850f9f4e7292612b2603f6768798a9510207
parent4b4006b8f21812017bec01c4be2c83030339cabb (diff)
parent980fd7cf48b231018e35b3097c54507fd591d2f9 (diff)
downloadpidgin-07b6a1878addfce61990c5f63e4535923ad323f7.tar.gz
Merge pidgin/main release-2.x.y branch in private/main release-2.x.y branch
No merge conflicts.
-rw-r--r--COPYRIGHT1
-rw-r--r--ChangeLog32
-rw-r--r--libpurple/certificate.c78
-rw-r--r--libpurple/certificate.h61
-rw-r--r--libpurple/plugins/ssl/ssl-gnutls.c37
-rw-r--r--libpurple/plugins/ssl/ssl-nss.c118
-rw-r--r--libpurple/protocols/jabber/jutil.c6
-rw-r--r--libpurple/protocols/mxit/markup.c45
-rw-r--r--libpurple/protocols/novell/nmevent.c29
-rw-r--r--pidgin/win32/untar.c58
10 files changed, 347 insertions, 118 deletions
diff --git a/COPYRIGHT b/COPYRIGHT
index 93ff039941..e0d00b963e 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -161,6 +161,7 @@ William Ehlhardt
Markus Elfring
Nelson Elhage
Ignacio J. Elia
+Kai Engert
Brian Enigma
Mattias Eriksson
Pat Erley
diff --git a/ChangeLog b/ChangeLog
index 86a76773e9..4029f2f808 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,15 +1,26 @@
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
-version 2.10.10 (?/?/?):
+version 2.10.10 (10/22/14):
General:
- * Allow and prefer TLS 1.2 and 1.1 when using libnss. (Elrond and
- Ashish Gupta) (#15909)
+ * Check the basic constraints extension when validating SSL/TLS
+ certificates. This fixes a security hole that allowed a malicious
+ man-in-the-middle to impersonate an IM server or any other https
+ endpoint. This affected both the NSS and GnuTLS plugins. (Discovered
+ by an anonymous person and Jacob Appelbaum of the Tor Project, with
+ thanks to Moxie Marlinspike for first publishing about this type of
+ vulnerability. Thanks to Kai Engert for guidance and for some of the
+ NSS changes) (CVE-2014-3694)
+ * Allow and prefer TLS 1.2 and 1.1 when using the NSS plugin for SSL.
+ (Elrond and Ashish Gupta) (#15909)
libpurple3 compatibility:
* Encrypted account passwords are preserved until the new one is set.
* Fix loading Google Talk and Facebook XMPP accounts.
Windows-Specific Changes:
+ * Don't allow overwriting arbitrary files on the file system when the
+ user installs a smiley theme via drag-and-drop. (Discovered by Yves
+ Younan of Cisco Talos) (CVE-2014-3697)
* Updates to dependencies:
* NSS 3.17.1 and NSPR 4.10.7
@@ -19,11 +30,26 @@ version 2.10.10 (?/?/?):
Gadu-Gadu:
* Updated internal libgadu to version 1.12.0.
+ Groupwise:
+ * Fix potential remote crash parsing server message that indicates that
+ a large amount of memory should be allocated. (Discovered by Yves Younan
+ and Richard Johnson of Cisco Talos) (CVE-2014-3696)
+
IRC:
* Fix a possible leak of unencrypted data when using /me command
with OTR. (Thijs Alkemade) (#15750)
+ MXit:
+ * Fix potential remote crash parsing a malformed emoticon response.
+ (Discovered by Yves Younan and Richard Johnson of Cisco Talos)
+ (CVE-2014-3695)
+
XMPP:
+ * Fix potential information leak where a malicious XMPP server and
+ possibly even a malicious remote user could create a carefully crafted
+ XMPP message that causes libpurple to send an XMPP message containing
+ arbitrary memory. (Discovered and fixed by Thijs Alkemade and Paul
+ Aurich) (CVE-2014-3698)
* Fix Facebook XMPP roster quirks. (#15041, #15957)
Yahoo:
diff --git a/libpurple/certificate.c b/libpurple/certificate.c
index b810359a92..ba71314ad4 100644
--- a/libpurple/certificate.c
+++ b/libpurple/certificate.c
@@ -41,50 +41,6 @@ static GList *cert_verifiers = NULL;
/** List of registered Pools */
static GList *cert_pools = NULL;
-/*
- * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
-typedef enum {
- PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
-
- /* Not an error */
- PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
-
- /* Non-fatal */
- PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
-
- /* The certificate is self-signed. */
- PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
-
- /* The CA is not in libpurple's pool of certificates. */
- PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
-
- /* The current time is before the certificate's specified
- * activation time.
- */
- PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
-
- /* The current time is after the certificate's specified expiration time */
- PURPLE_CERTIFICATE_EXPIRED = 0x08,
-
- /* The certificate's subject name doesn't match the expected */
- PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
-
- /* No CA pool was found. This shouldn't happen... */
- PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
-
- /* Fatal */
- PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
-
- /* The signature chain could not be validated. Due to limitations in the
- * the current API, this also indicates one of the CA certificates in the
- * chain is expired (or not yet activated). FIXME 3.0.0 */
- PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
-
- /* The signature has been revoked. */
- PURPLE_CERTIFICATE_REVOKED = 0x20000,
-
- PURPLE_CERTIFICATE_LAST = 0x40000,
-} PurpleCertificateInvalidityFlags;
static const gchar *
invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
@@ -776,10 +732,11 @@ static GList *x509_ca_certs = NULL;
/** Used for lazy initialization purposes. */
static gboolean x509_ca_initialized = FALSE;
-/** Adds a certificate to the in-memory cache, doing nothing else */
+/** Adds a certificate to the in-memory cache, and mark it as trusted */
static gboolean
x509_ca_quiet_put_cert(PurpleCertificate *crt)
{
+ gboolean ret;
x509_ca_element *el;
/* lazy_init calls this function, so calling lazy_init here is a
@@ -791,12 +748,20 @@ x509_ca_quiet_put_cert(PurpleCertificate *crt)
/* TODO: Perhaps just check crt->scheme->name instead? */
g_return_val_if_fail(crt->scheme == purple_certificate_find_scheme(x509_ca.scheme_name), FALSE);
- el = g_new0(x509_ca_element, 1);
- el->dn = purple_certificate_get_unique_id(crt);
- el->crt = purple_certificate_copy(crt);
- x509_ca_certs = g_list_prepend(x509_ca_certs, el);
+ ret = TRUE;
- return TRUE;
+ if (crt->scheme->register_trusted_tls_cert) {
+ ret = (crt->scheme->register_trusted_tls_cert)(crt, TRUE);
+ }
+
+ if (ret) {
+ el = g_new0(x509_ca_element, 1);
+ el->dn = purple_certificate_get_unique_id(crt);
+ el->crt = purple_certificate_copy(crt);
+ x509_ca_certs = g_list_prepend(x509_ca_certs, el);
+ }
+
+ return ret;
}
/* Since the libpurple CertificatePools get registered before plugins are
@@ -927,6 +892,7 @@ x509_ca_uninit(void)
g_list_free(x509_ca_certs);
x509_ca_certs = NULL;
x509_ca_initialized = FALSE;
+ /** TODO: the cert store in the SSL implementation wouldn't be cleared by this */
g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL);
g_list_free(x509_ca_paths);
x509_ca_paths = NULL;
@@ -1185,6 +1151,10 @@ x509_tls_peers_put_cert(const gchar *id, PurpleCertificate *crt)
keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);
ret = purple_certificate_export(keypath, crt);
+ if (crt->scheme->register_trusted_tls_cert) {
+ ret = (crt->scheme->register_trusted_tls_cert)(crt, FALSE);
+ }
+
g_free(keypath);
return ret;
}
@@ -1606,6 +1576,14 @@ x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
peer_crt = (PurpleCertificate *) chain->data;
+ if (peer_crt->scheme->verify_cert) {
+ /** Make sure we've loaded the CA certs (which causes NSS to trust them) */
+ g_return_if_fail(x509_ca_lazy_init());
+ peer_crt->scheme->verify_cert(vrq, &flags);
+ x509_tls_cached_complete(vrq, flags);
+ return;
+ }
+
/* TODO: Figure out a way to check for a bad signature, as opposed to
"not self-signed" */
if ( purple_certificate_signed_by(peer_crt, peer_crt) ) {
diff --git a/libpurple/certificate.h b/libpurple/certificate.h
index ee18adc6ca..727c533f05 100644
--- a/libpurple/certificate.h
+++ b/libpurple/certificate.h
@@ -46,6 +46,51 @@ typedef enum
PURPLE_CERTIFICATE_VALID = 1
} PurpleCertificateVerificationStatus;
+/*
+ * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
+typedef enum {
+ PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
+
+ /* Not an error */
+ PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
+
+ /* Non-fatal */
+ PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
+
+ /* The certificate is self-signed. */
+ PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
+
+ /* The CA is not in libpurple's pool of certificates. */
+ PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
+
+ /* The current time is before the certificate's specified
+ * activation time.
+ */
+ PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
+
+ /* The current time is after the certificate's specified expiration time */
+ PURPLE_CERTIFICATE_EXPIRED = 0x08,
+
+ /* The certificate's subject name doesn't match the expected */
+ PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
+
+ /* No CA pool was found. This shouldn't happen... */
+ PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
+
+ /* Fatal */
+ PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
+
+ /* The signature chain could not be validated. Due to limitations in the
+ * the current API, this also indicates one of the CA certificates in the
+ * chain is expired (or not yet activated). FIXME 3.0.0 */
+ PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
+
+ /* The signature has been revoked. */
+ PURPLE_CERTIFICATE_REVOKED = 0x20000,
+
+ PURPLE_CERTIFICATE_LAST = 0x40000,
+} PurpleCertificateInvalidityFlags;
+
typedef struct _PurpleCertificate PurpleCertificate;
typedef struct _PurpleCertificatePool PurpleCertificatePool;
typedef struct _PurpleCertificateScheme PurpleCertificateScheme;
@@ -197,7 +242,8 @@ struct _PurpleCertificateScheme
*/
void (* destroy_certificate)(PurpleCertificate * crt);
- /** Find whether "crt" has a valid signature from issuer "issuer"
+ /** Find whether "crt" has a valid signature from "issuer," including
+ * appropriate values for the CA flag in the basic constraints extension.
* @see purple_certificate_signed_by() */
gboolean (*signed_by)(PurpleCertificate *crt, PurpleCertificate *issuer);
/**
@@ -258,8 +304,17 @@ struct _PurpleCertificateScheme
*/
GSList * (* import_certificates)(const gchar * filename);
- void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
+ /**
+ * Register a certificate as "trusted."
+ */
+ gboolean (* register_trusted_tls_cert)(PurpleCertificate *crt, gboolean ca);
+
+ /**
+ * Verify that a certificate is valid, performing all necessary checks
+ * including date range, valid cert chain, recognized and valid CAs, etc.
+ */
+ void (* verify_cert)(PurpleCertificateVerificationRequest *vrq, PurpleCertificateInvalidityFlags *flags);
+
void (*_purple_reserved3)(void);
};
diff --git a/libpurple/plugins/ssl/ssl-gnutls.c b/libpurple/plugins/ssl/ssl-gnutls.c
index f3a72a4703..adb7e0c17a 100644
--- a/libpurple/plugins/ssl/ssl-gnutls.c
+++ b/libpurple/plugins/ssl/ssl-gnutls.c
@@ -925,7 +925,7 @@ x509_certificate_signed_by(PurpleCertificate * crt,
crt_dat = X509_GET_GNUTLS_DATA(crt);
issuer_dat = X509_GET_GNUTLS_DATA(issuer);
- /* First, let's check that crt.issuer is actually issuer */
+ /* Ensure crt issuer matches the name on the issuer cert. */
ret = gnutls_x509_crt_check_issuer(crt_dat, issuer_dat);
if (ret <= 0) {
@@ -954,6 +954,41 @@ x509_certificate_signed_by(PurpleCertificate * crt,
return FALSE;
}
+ /* Check basic constraints extension (if it exists then the CA flag must
+ be set to true, and it must exist for certs with version 3 or higher. */
+ ret = gnutls_x509_crt_get_basic_constraints(issuer_dat, NULL, NULL, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (gnutls_x509_crt_get_version(issuer_dat) >= 3) {
+ /* Reject cert (no basic constraints and cert version is >= 3). */
+ gchar *issuer_id = purple_certificate_get_unique_id(issuer);
+ purple_debug_info("gnutls/x509", "Rejecting cert because the "
+ "basic constraints extension is missing from issuer cert "
+ "for %s. The basic constraints extension is required on "
+ "all version 3 or higher certs (this cert is version %d).",
+ issuer_id ? issuer_id : "(null)",
+ gnutls_x509_crt_get_version(issuer_dat));
+ g_free(issuer_id);
+ return FALSE;
+ } else {
+ /* Allow cert (no basic constraints and cert version is < 3). */
+ purple_debug_info("gnutls/x509", "Basic constraint extension is "
+ "missing from issuer cert for %s. Allowing this because "
+ "the cert is version %d and the basic constraints "
+ "extension is only required for version 3 or higher "
+ "certs.", issuer_id ? issuer_id : "(null)",
+ gnutls_x509_crt_get_version(issuer_dat));
+ }
+ } else if (ret <= 0) {
+ /* Reject cert (CA flag is false in basic constraints). */
+ gchar *issuer_id = purple_certificate_get_unique_id(issuer);
+ purple_debug_info("gnutls/x509", "Rejecting cert because the CA flag "
+ "is set to false in the basic constraints extension for "
+ "issuer cert %s. ret=%d\n",
+ issuer_id ? issuer_id : "(null)", ret);
+ g_free(issuer_id);
+ return FALSE;
+ }
+
/* Now, check the signature */
/* The second argument is a ptr to an array of "trusted" issuer certs,
but we're only using one trusted one */
diff --git a/libpurple/plugins/ssl/ssl-nss.c b/libpurple/plugins/ssl/ssl-nss.c
index 76013334d4..87e8196de2 100644
--- a/libpurple/plugins/ssl/ssl-nss.c
+++ b/libpurple/plugins/ssl/ssl-nss.c
@@ -45,6 +45,7 @@
#include <nspr.h>
#include <nss.h>
#include <nssb64.h>
+#include <ocsp.h>
#include <pk11func.h>
#include <prio.h>
#include <secerr.h>
@@ -53,6 +54,11 @@
#include <sslerr.h>
#include <sslproto.h>
+/* There's a bug in some versions of this header that requires that some of
+ the headers above be included first. This is true for at least libnss
+ 3.15.4. */
+#include <certdb.h>
+
/* This is defined in NSPR's <private/pprio.h>, but to avoid including a
* private header we duplicate the prototype here */
NSPR_API(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd);
@@ -182,6 +188,9 @@ ssl_nss_init_nss(void)
}
#endif /* NSS >= 3.14 */
+ /** Disable OCSP Checking until we can make that use our HTTP & Proxy stuff */
+ CERT_EnableOCSPChecking(PR_FALSE);
+
_identity = PR_GetUniqueIdentity("Purple");
_nss_methods = PR_GetDefaultIOMethods();
}
@@ -952,7 +961,7 @@ x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
Handling is different for signed vs unsigned 32-bit types.
*/
if (*activation != nss_activ) {
- if (nss_activ < 0) {
+ if (nss_activ < 0) {
purple_debug_warning("nss",
"Setting Activation Date to epoch to handle pre-epoch value\n");
*activation = 0;
@@ -990,6 +999,108 @@ x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
return TRUE;
}
+static gboolean
+x509_register_trusted_tls_cert(PurpleCertificate *crt, gboolean ca)
+{
+ CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
+ CERTCertificate *crt_dat;
+ CERTCertTrust trust;
+
+ g_return_val_if_fail(crt, FALSE);
+ g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
+
+ crt_dat = X509_NSS_DATA(crt);
+ g_return_val_if_fail(crt_dat, FALSE);
+
+ purple_debug_info("nss", "Trusting %s\n", crt_dat->subjectName);
+
+ if (ca && !CERT_IsCACert(crt_dat, NULL)) {
+ purple_debug_error("nss",
+ "Refusing to set non-CA cert as trusted CA\n");
+ return FALSE;
+ }
+
+ if (crt_dat->isperm) {
+ purple_debug_info("nss",
+ "Skipping setting trust for cert in permanent DB\n");
+ return TRUE;
+ }
+
+ if (ca) {
+ trust.sslFlags = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
+ } else {
+ trust.sslFlags = CERTDB_TRUSTED;
+ }
+ trust.emailFlags = 0;
+ trust.objectSigningFlags = 0;
+
+ CERT_ChangeCertTrust(certdb, crt_dat, &trust);
+
+ return TRUE;
+}
+
+static void x509_verify_cert(PurpleCertificateVerificationRequest *vrq, PurpleCertificateInvalidityFlags *flags)
+{
+ CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
+ CERTCertificate *crt_dat;
+ PRTime now = PR_Now();
+ SECStatus rv;
+ PurpleCertificate *first_cert = vrq->cert_chain->data;
+ CERTVerifyLog log;
+
+ crt_dat = X509_NSS_DATA(first_cert);
+
+ log.arena = PORT_NewArena(512);
+ log.head = log.tail = NULL;
+ log.count = 0;
+ rv = CERT_VerifyCert(certdb, crt_dat, PR_TRUE, certUsageSSLServer, now, NULL, &log);
+
+ if (rv != SECSuccess || log.count > 0) {
+ CERTVerifyLogNode *node = NULL;
+ unsigned int depth = (unsigned int)-1;
+
+ for (node = log.head; node; node = node->next) {
+ if (depth != node->depth) {
+ depth = node->depth;
+ purple_debug_error("nss", "CERT %d. %s %s:\n", depth,
+ node->cert->subjectName,
+ depth ? "[Certificate Authority]": "");
+ }
+ purple_debug_error("nss", " ERROR %ld: %s\n", node->error,
+ PR_ErrorToName(node->error));
+ switch (node->error) {
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ *flags |= PURPLE_CERTIFICATE_EXPIRED;
+ break;
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ *flags |= PURPLE_CERTIFICATE_REVOKED;
+ break;
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ if (crt_dat->isRoot) {
+ *flags |= PURPLE_CERTIFICATE_SELF_SIGNED;
+ } else {
+ *flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
+ }
+ break;
+ case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
+ case SEC_ERROR_BAD_SIGNATURE:
+ default:
+ *flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
+ }
+ if (node->cert)
+ CERT_DestroyCertificate(node->cert);
+ }
+ } else {
+ rv = CERT_VerifyCertName(crt_dat, vrq->subject_name);
+ if (rv != SECSuccess) {
+ purple_debug_error("nss", "Cert chain valid, but name not verified\n");
+ *flags |= PURPLE_CERTIFICATE_NAME_MISMATCH;
+ }
+ }
+
+ PORT_FreeArena(log.arena, PR_FALSE);
+}
+
static PurpleCertificateScheme x509_nss = {
"x509", /* Scheme name */
N_("X.509 Certificates"), /* User-visible scheme name */
@@ -1005,9 +1116,8 @@ static PurpleCertificateScheme x509_nss = {
x509_check_name, /* Check subject name */
x509_times, /* Activation/Expiration time */
x509_importcerts_from_file, /* Multiple certificate import function */
-
- NULL,
- NULL,
+ x509_register_trusted_tls_cert, /* Register a certificate as trusted for TLS */
+ x509_verify_cert, /* Verify that the specified cert chain is trusted */
NULL
};
diff --git a/libpurple/protocols/jabber/jutil.c b/libpurple/protocols/jabber/jutil.c
index 6ad62b0ea7..649beab1f4 100644
--- a/libpurple/protocols/jabber/jutil.c
+++ b/libpurple/protocols/jabber/jutil.c
@@ -81,10 +81,10 @@ jabber_idn_validate(const char *str, const char *at, const char *slash,
if (slash) {
domain_len = slash - str;
- resource = slash;
+ resource = slash + 1;
resource_len = null - (slash + 1);
} else {
- domain_len = null - (str + 1);
+ domain_len = null - str;
}
}
@@ -126,6 +126,8 @@ jabber_idn_validate(const char *str, const char *at, const char *slash,
jid = NULL;
goto out;
}
+
+ jid->domain = g_strndup(domain, domain_len);
} else {
/* Apply nameprep */
if (stringprep_nameprep(idn_buffer, sizeof(idn_buffer)) != STRINGPREP_OK) {
diff --git a/libpurple/protocols/mxit/markup.c b/libpurple/protocols/mxit/markup.c
index 4e249fdd2e..f1fa99a2c6 100644
--- a/libpurple/protocols/mxit/markup.c
+++ b/libpurple/protocols/mxit/markup.c
@@ -163,16 +163,22 @@ void mxit_add_html_link( struct RXMsgData* mx, const char* replydata, gboolean i
* Extract an ASN.1 formatted length field from the data.
*
* @param data The source data
+ * @param data_len Length of data
* @param size The extracted length
* @return The number of bytes extracted
*/
-static unsigned int asn_getlength( const gchar* data, int* size )
+static unsigned int asn_getlength( const gchar* data, gsize data_len, int* size )
{
unsigned int len = 0;
unsigned char bytes;
unsigned char byte;
int i;
+ if ( data_len < 1 ) {
+ /* missing first byte! */
+ return -1;
+ }
+
/* first byte specifies the number of bytes in the length */
bytes = ( data[0] & ~0x80 );
if ( bytes > sizeof( unsigned int ) ) {
@@ -181,6 +187,11 @@ static unsigned int asn_getlength( const gchar* data, int* size )
}
data++;
+ if ( data_len - 1 < bytes ) {
+ /* missing length! */
+ return -1;
+ }
+
/* parse out the actual length */
for ( i = 0; i < bytes; i++ ) {
byte = data[i];
@@ -197,15 +208,21 @@ static unsigned int asn_getlength( const gchar* data, int* size )
* Extract an ASN.1 formatted UTF-8 string field from the data.
*
* @param data The source data
+ * @param data_len Length of data
* @param type Expected type of string
* @param utf8 The extracted string. Must be deallocated by caller.
* @return The number of bytes extracted
*/
-static int asn_getUtf8( const gchar* data, gchar type, char** utf8 )
+static int asn_getUtf8( const gchar* data, gsize data_len, gchar type, char** utf8 )
{
unsigned int len;
gchar *out_str;
+ if ( data_len < 2 ) {
+ /* missing type or length! */
+ return -1;
+ }
+
/* validate the field type [1 byte] */
if ( data[0] != type ) {
/* this is not a utf-8 string! */
@@ -214,6 +231,11 @@ static int asn_getUtf8( const gchar* data, gchar type, char** utf8 )
}
len = (guint8)data[1]; /* length field [1 byte] */
+ if ( data_len - 2 < len ) {
+ /* not enough bytes left in data! */
+ return -1;
+ }
+
out_str = g_malloc(len + 1);
memcpy(out_str, &data[2], len); /* data field */
out_str[len] = '\0';
@@ -500,7 +522,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
#endif
/* validate that the returned data starts with the magic constant that indicates it is a custom emoticon */
- if ( memcmp( MXIT_FRAME_MAGIC, &data[pos], strlen( MXIT_FRAME_MAGIC ) ) != 0 ) {
+ if ( len - pos < strlen( MXIT_FRAME_MAGIC ) || memcmp( MXIT_FRAME_MAGIC, &data[pos], strlen( MXIT_FRAME_MAGIC ) ) != 0 ) {
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad magic)\n" );
goto done;
}
@@ -514,7 +536,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
pos++;
/* get the frame image data length */
- res = asn_getlength( &data[pos], &em_size );
+ res = asn_getlength( &data[pos], len - pos, &em_size );
if ( res <= 0 ) {
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad frame length)\n" );
goto done;
@@ -525,7 +547,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
#endif
/* utf-8 (emoticon name) */
- res = asn_getUtf8( &data[pos], 0x0C, &str );
+ res = asn_getUtf8( &data[pos], len - pos, 0x0C, &str );
if ( res <= 0 ) {
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad name string)\n" );
goto done;
@@ -538,7 +560,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
str = NULL;
/* utf-8 (emoticon shortcut) */
- res = asn_getUtf8( &data[pos], 0x81, &str );
+ res = asn_getUtf8( &data[pos], len - pos, 0x81, &str );
if ( res <= 0 ) {
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad shortcut string)\n" );
goto done;
@@ -550,7 +572,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
em_id = str;
/* validate the image data type */
- if ( data[pos] != '\x82' ) {
+ if ( len - pos < 1 || data[pos] != '\x82' ) {
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad data type)\n" );
g_free( em_id );
goto done;
@@ -558,7 +580,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
pos++;
/* get the data length */
- res = asn_getlength( &data[pos], &em_size );
+ res = asn_getlength( &data[pos], len - pos, &em_size );
if ( res <= 0 ) {
/* bad frame length */
purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad data length)\n" );
@@ -570,6 +592,13 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
purple_debug_info( MXIT_PLUGIN_ID, "read the length '%i'\n", em_size );
#endif
+ if ( len - pos < em_size ) {
+ /* not enough bytes left in data! */
+ purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (data length too long)\n");
+ g_free( em_id );
+ goto done;
+ }
+
/* strip the mxit markup tags from the emoticon id (eg, .{XY} -> XY) */
if ( ( em_id[0] == '.' ) && ( em_id[1] == '{' ) ) {
char emo[MXIT_MAX_EMO_ID + 1];
diff --git a/libpurple/protocols/novell/nmevent.c b/libpurple/protocols/novell/nmevent.c
index 5a8674d4f9..5268b96813 100644
--- a/libpurple/protocols/novell/nmevent.c
+++ b/libpurple/protocols/novell/nmevent.c
@@ -149,7 +149,7 @@ handle_receive_message(NMUser * user, NMEvent * event, gboolean autoreply)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -164,7 +164,7 @@ handle_receive_message(NMUser * user, NMEvent * event, gboolean autoreply)
/* Read the message text */
if (rc == NM_OK) {
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 100000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
msg = g_new0(char, size + 1);
@@ -270,7 +270,7 @@ handle_conference_invite(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -280,7 +280,7 @@ handle_conference_invite(NMUser * user, NMEvent * event)
/* Read the the message */
if (rc == NM_OK) {
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 100000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
msg = g_new0(char, size + 1);
@@ -349,7 +349,7 @@ handle_conference_invite_notify(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -401,7 +401,7 @@ handle_conference_reject(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -440,7 +440,7 @@ handle_conference_left(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -490,7 +490,7 @@ handle_conference_closed(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -530,7 +530,7 @@ handle_conference_joined(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -589,7 +589,7 @@ handle_typing(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -632,7 +632,7 @@ handle_status_change(NMUser * user, NMEvent * event)
/* Read the status text */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 10000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
text = g_new0(char, size + 1);
@@ -670,7 +670,7 @@ handle_undeliverable_status(NMUser * user, NMEvent * event)
/* Read the conference guid */
rc = nm_read_uint32(conn, &size);
- if (size == MAX_UINT32) return NMERR_PROTOCOL;
+ if (size > 1000) return NMERR_PROTOCOL;
if (rc == NM_OK) {
guid = g_new0(char, size + 1);
@@ -833,7 +833,10 @@ nm_process_event(NMUser * user, int type)
/* Read the event source */
rc = nm_read_uint32(conn, &size);
if (rc == NM_OK) {
- if (size > 0) {
+ if (size > 1000000) {
+ /* Size is larger than our 1MB sanity check. Ignore it. */
+ rc = NMERR_PROTOCOL;
+ } else {
source = g_new0(char, size);
rc = nm_read_all(conn, source, size);
diff --git a/pidgin/win32/untar.c b/pidgin/win32/untar.c
index db1fa1bd4e..6a5b4440e0 100644
--- a/pidgin/win32/untar.c
+++ b/pidgin/win32/untar.c
@@ -122,8 +122,6 @@ static const char *inname = NULL; /* name of input archive */
static FILE *infp = NULL; /* input byte stream */
static FILE *outfp = NULL; /* output stream, for file currently being extracted */
static Ulong_t outsize = 0; /* number of bytes remainin in file currently being extracted */
-static char **only = NULL; /* array of filenames to extract/list */
-static int nonlys = 0; /* number of filenames in "only" array; 0=extract all */
static int didabs = 0; /* were any filenames affected by the absence of -p? */
static untar_opt untarops = 0; /* Untar options */
@@ -323,7 +321,7 @@ static long checksum(tblk, sunny)
/* list files in an archive, and optionally extract them as well */
static int untar_block(Uchar_t *blk) {
- static char nbuf[256];/* storage space for prefix+name, combined */
+ static char nbuf[4096];/* storage space for prefix+name, combined */
static char *name,*n2;/* prefix and name, combined */
static int first = 1;/* Boolean: first block of archive? */
long sum; /* checksum for this block */
@@ -392,24 +390,35 @@ static int untar_block(Uchar_t *blk) {
/* combine prefix and filename */
memset(nbuf, 0, sizeof nbuf);
- name = nbuf;
if ((tblk)->prefix[0])
{
- strncpy(name, (tblk)->prefix, sizeof (tblk)->prefix);
- strcat(name, "/");
- strncat(name + strlen(name), (tblk)->filename,
- sizeof (tblk)->filename);
+ snprintf(nbuf, sizeof(nbuf), "%s/%s",
+ (tblk)->prefix, (tblk)->filename);
}
else
{
- strncpy(name, (tblk)->filename,
- sizeof (tblk)->filename);
+ g_strlcpy(nbuf, (tblk)->filename,
+ sizeof (nbuf));
+ }
+
+ /* Possibly strip the drive from the path */
+ if (!ABSPATH) {
+ /* If the path contains a colon, assume everything before the
+ * colon is intended to be a drive name and ignore it. This
+ * should be just a single drive letter, but it should be safe
+ * to drop it even if it's longer. */
+ const char *lastcolon = strrchr(nbuf, ':');
+ if (lastcolon) {
+ memmove(nbuf, lastcolon, strlen(lastcolon) + 1);
+ didabs = 1; /* Path was changed from absolute to relative */
+ }
}
/* Convert any backslashes to forward slashes, and guard
* against doubled-up slashes. (Some DOS versions of "tar"
* get this wrong.) Also strip off leading slashes.
*/
+ name = nbuf;
if (!ABSPATH && (*name == '/' || *name == '\\'))
didabs = 1;
for (n2 = nbuf; *name; name++)
@@ -474,26 +483,6 @@ static int untar_block(Uchar_t *blk) {
}
#endif
- /* If we have an "only" list, and this file isn't in it,
- * then skip it.
- */
- if (nonlys > 0)
- {
- for (i = 0;
- i < nonlys
- && strcmp(only[i], nbuf)
- && (strncmp(only[i], nbuf, strlen(only[i]))
- || nbuf[strlen(only[i])] != '/');
- i++)
- {
- }
- if (i >= nonlys)
- {
- outfp = NULL;
- return 1;
- }
- }
-
/* list the file */
if (VERBOSE)
untar_verbose("%c %s",
@@ -520,18 +509,19 @@ static int untar_block(Uchar_t *blk) {
*/
if (tblk->type == '5')
{
+ char *tmp;
if (LISTING)
- n2 = " directory";
+ tmp = " directory";
#ifdef _POSIX_SOURCE
else if (mkdir(nbuf, mode) == 0)
#else
else if (g_mkdir(nbuf, 0755) == 0)
#endif
- n2 = " created";
+ tmp = " created";
else
- n2 = " ignored";
+ tmp = " ignored";
if (VERBOSE)
- untar_verbose("%s\n", n2);
+ untar_verbose("%s\n", tmp);
return 1;
}