diff options
author | Mark Doliner <mark@kingant.net> | 2014-10-16 16:01:07 -0700 |
---|---|---|
committer | Mark Doliner <mark@kingant.net> | 2014-10-16 16:01:07 -0700 |
commit | 07b6a1878addfce61990c5f63e4535923ad323f7 (patch) | |
tree | 4c16850f9f4e7292612b2603f6768798a9510207 | |
parent | 4b4006b8f21812017bec01c4be2c83030339cabb (diff) | |
parent | 980fd7cf48b231018e35b3097c54507fd591d2f9 (diff) | |
download | pidgin-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-- | COPYRIGHT | 1 | ||||
-rw-r--r-- | ChangeLog | 32 | ||||
-rw-r--r-- | libpurple/certificate.c | 78 | ||||
-rw-r--r-- | libpurple/certificate.h | 61 | ||||
-rw-r--r-- | libpurple/plugins/ssl/ssl-gnutls.c | 37 | ||||
-rw-r--r-- | libpurple/plugins/ssl/ssl-nss.c | 118 | ||||
-rw-r--r-- | libpurple/protocols/jabber/jutil.c | 6 | ||||
-rw-r--r-- | libpurple/protocols/mxit/markup.c | 45 | ||||
-rw-r--r-- | libpurple/protocols/novell/nmevent.c | 29 | ||||
-rw-r--r-- | pidgin/win32/untar.c | 58 |
10 files changed, 347 insertions, 118 deletions
@@ -161,6 +161,7 @@ William Ehlhardt Markus Elfring Nelson Elhage Ignacio J. Elia +Kai Engert Brian Enigma Mattias Eriksson Pat Erley @@ -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; } |