[Spice-commits] 8 commits - client/application.cpp client/red_client.cpp client/red_peer.cpp client/red_peer.h client/windows common/canvas_base.h common/canvas_utils.c common/canvas_utils.h common/Makefile.am common/mem.c common/mem.h common/ring.h common/ssl_verify.c common/ssl_verify.h configure.ac server/Makefile.am server/tests

Marc-André Lureau elmarco at kemper.freedesktop.org
Tue May 3 08:31:53 PDT 2011


 client/application.cpp       |   19 -
 client/red_client.cpp        |    2 
 client/red_peer.cpp          |  377 +--------------------------------
 client/red_peer.h            |   23 --
 client/windows/platform.cpp  |    1 
 common/Makefile.am           |    2 
 common/canvas_base.h         |    3 
 common/canvas_utils.c        |    2 
 common/canvas_utils.h        |    4 
 common/mem.c                 |    4 
 common/mem.h                 |   30 ++
 common/ring.h                |   13 +
 common/ssl_verify.c          |  481 +++++++++++++++++++++++++++++++++++++++++++
 common/ssl_verify.h          |   60 +++++
 configure.ac                 |    5 
 server/Makefile.am           |    1 
 server/tests/test_playback.c |    1 
 17 files changed, 640 insertions(+), 388 deletions(-)

New commits:
commit 138c4211936503c66eee81822eaaba8e9e727318
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue May 3 15:40:48 2011 +0200

    build: fix gettimeofday warning
    
      CC     test_playback.o
    test_playback.c: In function ‘playback_timer_cb’:
    test_playback.c:56:5: warning: implicit declaration of function ‘gettimeofday’

diff --git a/server/tests/test_playback.c b/server/tests/test_playback.c
index 18220ea..6f332a9 100644
--- a/server/tests/test_playback.c
+++ b/server/tests/test_playback.c
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <strings.h>
 #include <sys/select.h>
+#include <sys/time.h>
 #include <math.h>
 
 #include <spice.h>
commit d46f9d3f4e006d3bca9b99fac25169b17e7ac803
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue May 3 16:14:18 2011 +0200

    client: make use of ssl_verify.c
    
    Fixed since v1:
    - don't include C code, rather use the common lib
    - add missing spice_openssl_verify_free() call
    - keep the extra-parsing of subject for error reporting

diff --git a/client/application.cpp b/client/application.cpp
index bc6a6ee..e308ad1 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -354,7 +354,7 @@ Application::Application()
     , _monitors (NULL)
     , _title ("SPICEc:%d")
     , _sys_key_intercept_mode (false)
-	, _enable_controller (false)
+    , _enable_controller (false)
 #ifdef USE_GUI
     , _gui_mode (GUI_MODE_FULL)
 #endif // USE_GUI
@@ -387,7 +387,7 @@ Application::Application()
     _canvas_types[0] = CANVAS_OPTION_SW;
 #endif
 
-    _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_NAME;
+    _host_auth_opt.type_flags = SPICE_SSL_VERIFY_OP_HOSTNAME;
 
     Platform::get_app_data_dir(_host_auth_opt.CA_file, app_name);
     Platform::path_append(_host_auth_opt.CA_file, CA_FILE_NAME);
@@ -1993,9 +1993,11 @@ bool Application::set_host_cert_subject(const char* subject, const char* arg0)
     std::string subject_str(subject);
     std::string::const_iterator iter = subject_str.begin();
     std::string entry;
-    _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT;
-    _host_auth_opt.host_subject.clear();
+    _host_auth_opt.type_flags = SPICE_SSL_VERIFY_OP_SUBJECT;
+    _host_auth_opt.host_subject = subject;
 
+    /* the follow is only checking code, subject is parsed later
+       ssl_verify.c.  We keep simply because of better error message... */
     while (true) {
         if ((iter == subject_str.end()) || (*iter == ',')) {
             RedPeer::HostAuthOptions::CertFieldValuePair entry_pair;
@@ -2015,7 +2017,6 @@ bool Application::set_host_cert_subject(const char* subject, const char* arg0)
             }
             entry_pair.first = entry.substr(start_pos, value_pos - start_pos);
             entry_pair.second = entry.substr(value_pos + 1);
-            _host_auth_opt.host_subject.push_back(entry_pair);
             DBG(0, "subject entry: %s=%s", entry_pair.first.c_str(), entry_pair.second.c_str());
             if (iter == subject_str.end()) {
                 break;
@@ -2039,6 +2040,7 @@ bool Application::set_host_cert_subject(const char* subject, const char* arg0)
         }
         iter++;
     }
+
     return true;
 }
 
@@ -2284,8 +2286,9 @@ bool Application::process_cmd_line(int argc, char** argv, bool &full_screen)
 #ifdef USE_SMARTCARD
     parser.add(SPICE_OPT_SMARTCARD, "smartcard", "enable smartcard channel");
     parser.add(SPICE_OPT_NOSMARTCARD, "nosmartcard", "disable smartcard channel");
-    parser.add(SPICE_OPT_SMARTCARD_CERT, "smartcard-cert", "Use virtual reader+card with given cert(s)",
-        "smartcard-cert", true);
+    parser.add(SPICE_OPT_SMARTCARD_CERT, "smartcard-cert",
+               "Use virtual reader+card with given cert(s)",
+               "smartcard-cert", true);
     parser.set_multi(SPICE_OPT_SMARTCARD_CERT, ',');
     parser.add(SPICE_OPT_SMARTCARD_DB, "smartcard-db", "Use given db for smartcard certs", "smartcard-db", true);
 #endif
@@ -2516,7 +2519,7 @@ void spice_log(unsigned int type, const char *function, const char *format, ...)
                 Platform::get_thread_id(),
                 function_to_func_name(function).c_str(),
                 formated_message.c_str());
-		fflush(log_file);
+        fflush(log_file);
     }
 
     if (type >= LOG_WARN) {
diff --git a/client/red_client.cpp b/client/red_client.cpp
index 56a3e22..8918e4f 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -274,7 +274,7 @@ void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
         _host.assign((char *)migrate->host_data);
         _port = migrate->port ? migrate->port : -1;
         _sport = migrate->sport ? migrate->sport : -1;
-        _auth_options.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY;
+        _auth_options.type_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
         _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data + migrate->pub_key_size);
     }
 
diff --git a/client/red_peer.cpp b/client/red_peer.cpp
index 19919a6..37ca2b8 100644
--- a/client/red_peer.cpp
+++ b/client/red_peer.cpp
@@ -27,12 +27,7 @@
 #include "utils.h"
 #include "debug.h"
 #include "platform_utils.h"
-
-typedef struct SslVerifyCbData {
-    RedPeer::HostAuthOptions info;
-    const char* host_name;
-    bool all_preverify_ok;
-} SslVerifyCbData;
+#include "ssl_verify.h"
 
 static void ssl_error()
 {
@@ -135,346 +130,11 @@ void RedPeer::connect_unsecure(const char* host, int portnr)
     }
 }
 
-bool RedPeer::verify_pubkey(X509* cert, const HostAuthOptions::PublicKey& key)
-{
-    EVP_PKEY* cert_pubkey = NULL;
-    EVP_PKEY* orig_pubkey = NULL;
-    BIO* bio = NULL;
-    uint8_t* c_key = NULL;
-    int ret = 0;
-
-    if (key.empty()) {
-        return false;
-    }
-
-    ASSERT(cert);
-
-    try {
-        cert_pubkey = X509_get_pubkey(cert);
-        if (!cert_pubkey) {
-            THROW("reading public key from certificate failed");
-        }
-
-        c_key = new uint8_t[key.size()];
-        memcpy(c_key, &key[0], key.size());
-
-        bio = BIO_new_mem_buf((void*)c_key, key.size());
-        if (!bio) {
-            THROW("creating BIO failed");
-        }
-
-        orig_pubkey = d2i_PUBKEY_bio(bio, NULL);
-        if (!orig_pubkey) {
-            THROW("reading pubkey from bio failed");
-        }
-
-        ret = EVP_PKEY_cmp(orig_pubkey, cert_pubkey);
-
-        BIO_free(bio);
-        EVP_PKEY_free(orig_pubkey);
-        EVP_PKEY_free(cert_pubkey);
-        delete []c_key;
-        if (ret == 1) {
-            DBG(0, "public keys match");
-            return true;
-        } else if (ret == 0) {
-            DBG(0, "public keys mismatch");
-            return false;
-        } else {
-            DBG(0, "public keys types mismatch");
-            return false;
-        }
-    } catch (Exception& e) {
-        LOG_WARN("%s", e.what());
-
-        if (bio) {
-            BIO_free(bio);
-        }
-
-        if (orig_pubkey) {
-            EVP_PKEY_free(orig_pubkey);
-        }
-
-        if (cert_pubkey) {
-            EVP_PKEY_free(cert_pubkey);
-        }
-        delete []c_key;
-        return false;
-    }
-}
-
-/* From gnutls: compare host_name against certificate, taking account of wildcards.
- * return true on success or false on error.
- *
- * note: cert_name_size is required as X509 certs can contain embedded NULs in
- * the strings such as CN or subjectAltName
- */
-bool RedPeer::x509_cert_host_name_compare(const char *cert_name, int cert_name_size,
-                                          const char *host_name)
-{
-    /* find the first different character */
-    for (; *cert_name && *host_name && (toupper(*cert_name) == toupper(*host_name));
-         cert_name++, host_name++, cert_name_size--);
-
-    /* the strings are the same */
-    if (cert_name_size == 0 && *host_name == '\0')
-        return true;
-
-    if (*cert_name == '*')
-    {
-        /* a wildcard certificate */
-        cert_name++;
-        cert_name_size--;
-
-        while (true)
-        {
-            /* Use a recursive call to allow multiple wildcards */
-            if (RedPeer::x509_cert_host_name_compare(cert_name, cert_name_size, host_name)) {
-                return true;
-            }
-
-            /* wildcards are only allowed to match a single domain
-               component or component fragment */
-            if (*host_name == '\0' || *host_name == '.')
-                break;
-            host_name++;
-        }
-
-        return false;
-    }
-
-  return false;
-}
-
-/*
- * From gnutls_x509_crt_check_hostname - compares the hostname with certificate's hostname
- *
- * This function will check if the given certificate's subject matches
- * the hostname.  This is a basic implementation of the matching
- * described in RFC2818 (HTTPS), which takes into account wildcards,
- * and the DNSName/IPAddress subject alternative name PKIX extension.
- *
- */
-bool RedPeer::verify_host_name(X509* cert, const char* host_name)
-{
-    GENERAL_NAMES* subject_alt_names;
-    bool found_dns_name = false;
-    struct in_addr addr;
-    int addr_len = 0;
-    bool cn_match = false;
-
-    ASSERT(cert);
-
-    // only IpV4 supported
-    if (inet_aton(host_name, &addr)) {
-        addr_len = sizeof(struct in_addr);
-    }
-
-    /* try matching against:
-    *  1) a DNS name or IP address as an alternative name (subjectAltName) extension
-    *     in the certificate
-    *  2) the common name (CN) in the certificate
-    *
-    *  either of these may be of the form: *.domain.tld
-    *
-    *  only try (2) if there is no subjectAltName extension of
-    *  type dNSName
-    */
-
-
-    subject_alt_names = (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-
-    if (subject_alt_names) {
-        int num_alts = sk_GENERAL_NAME_num(subject_alt_names);
-        for (int i = 0; i < num_alts; i++) {
-            const GENERAL_NAME* name = sk_GENERAL_NAME_value(subject_alt_names, i);
-            if (name->type == GEN_DNS) {
-                found_dns_name = true;
-                if (RedPeer::x509_cert_host_name_compare((char *)ASN1_STRING_data(name->d.dNSName),
-                                                         ASN1_STRING_length(name->d.dNSName),
-                                                         host_name)) {
-                    DBG(0, "alt name match=%s", ASN1_STRING_data(name->d.dNSName));
-                    GENERAL_NAMES_free(subject_alt_names);
-                    return true;
-                }
-            } else if (name->type == GEN_IPADD) {
-                int alt_ip_len = ASN1_STRING_length(name->d.iPAddress);
-                found_dns_name = true;
-                if ((addr_len == alt_ip_len)&&
-                    !memcmp(ASN1_STRING_data(name->d.iPAddress), &addr, addr_len)) {
-                         DBG(0, "alt name IP match=%s",
-                             inet_ntoa(*((struct in_addr*)ASN1_STRING_data(name->d.dNSName))));
-                    GENERAL_NAMES_free(subject_alt_names);
-                    return true;
-                }
-            }
-        }
-        GENERAL_NAMES_free(subject_alt_names);
-    }
-
-    if (found_dns_name)
-    {
-        DBG(0, "SubjectAltName mismatch");
-        return false;
-    }
-
-    /* extracting commonNames */
-    X509_NAME* subject = X509_get_subject_name(cert);
-    if (subject) {
-        int pos = -1;
-        X509_NAME_ENTRY* cn_entry;
-        ASN1_STRING* cn_asn1;
-
-        while ((pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos)) != -1) {
-            cn_entry = X509_NAME_get_entry(subject, pos);
-            if (!cn_entry) {
-                continue;
-            }
-            cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
-            if (!cn_asn1) {
-                continue;
-            }
-
-            if (RedPeer::x509_cert_host_name_compare((char*)ASN1_STRING_data(cn_asn1),
-                                                     ASN1_STRING_length(cn_asn1),
-                                                     host_name)) {
-                DBG(0, "common name match=%s", (char*)ASN1_STRING_data(cn_asn1));
-                cn_match = true;
-                break;
-            }
-        }
-    }
-
-    if (!cn_match) {
-        DBG(0, "common name mismatch");
-    }
-    return cn_match;
-
-}
-
-bool RedPeer::verify_subject(X509* cert, const HostAuthOptions::CertFieldValueList& subject)
-{
-    X509_NAME* cert_subject = NULL;
-    HostAuthOptions::CertFieldValueList::const_iterator subject_iter;
-    X509_NAME* in_subject;
-    int ret;
-
-    ASSERT(cert);
-
-    cert_subject = X509_get_subject_name(cert);
-    if (!cert_subject) {
-        LOG_WARN("reading certificate subject failed");
-        return false;
-    }
-
-    if ((size_t)X509_NAME_entry_count(cert_subject) != subject.size()) {
-        LOG_ERROR("subject mismatch: #entries cert=%d, input=%d",
-            X509_NAME_entry_count(cert_subject), subject.size());
-        return false;
-    }
-
-    in_subject = X509_NAME_new();
-    if (!in_subject) {
-        LOG_WARN("failed to allocate X509_NAME");
-        return false;
-    }
-
-    for (subject_iter = subject.begin(); subject_iter != subject.end(); subject_iter++) {
-        if (!X509_NAME_add_entry_by_txt(in_subject,
-                                        subject_iter->first.c_str(),
-                                        MBSTRING_UTF8,
-                                        (const unsigned char*)subject_iter->second.c_str(),
-                                        subject_iter->second.length(), -1, 0)) {
-            LOG_WARN("failed to add entry %s=%s to X509_NAME",
-                     subject_iter->first.c_str(), subject_iter->second.c_str());
-             X509_NAME_free(in_subject);
-             return false;
-        }
-    }
-
-    ret = X509_NAME_cmp(cert_subject, in_subject);
-    X509_NAME_free(in_subject);
-
-    if (ret == 0) {
-         DBG(0, "subjects match");
-         return true;
-    } else {
-         LOG_ERROR("host-subject mismatch");
-         return false;
-    }
-}
-
-int RedPeer::ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
-{
-    int depth;
-    SSL *ssl;
-    X509* cert;
-    SslVerifyCbData* verify_data;
-    int auth_flags;
-
-    depth = X509_STORE_CTX_get_error_depth(ctx);
-
-    ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-    if (!ssl) {
-        LOG_WARN("failed to get ssl connection");
-        return 0;
-    }
-
-    verify_data = (SslVerifyCbData*)SSL_get_app_data(ssl);
-    auth_flags = verify_data->info.type_flags;
-
-    if (depth > 0) {
-        // if certificate verification failed, we can still authorize the server
-        // if its public key matches the one we hold in the peer_connect_options.
-        if (!preverify_ok) {
-            DBG(0, "openssl verify failed at depth=%d", depth);
-            verify_data->all_preverify_ok = false;
-            if (auth_flags & HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
-                return 1;
-            } else {
-                return 0;
-            }
-        } else {
-            return preverify_ok;
-        }
-    }
-
-    /* depth == 0 */
-    cert = X509_STORE_CTX_get_current_cert(ctx);
-    if (!cert) {
-        LOG_WARN("failed to get server certificate");
-        return 0;
-    }
-
-    if (auth_flags & HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
-        if (verify_pubkey(cert, verify_data->info.host_pubkey)) {
-            return 1;
-        }
-    }
-
-    if (!verify_data->all_preverify_ok || !preverify_ok) {
-        return 0;
-    }
-
-    if (auth_flags & HostAuthOptions::HOST_AUTH_OP_NAME) {
-        if (verify_host_name(cert, verify_data->host_name)) {
-            return 1;
-        }
-    }
-
-    if (auth_flags & HostAuthOptions::HOST_AUTH_OP_SUBJECT) {
-        if (verify_subject(cert, verify_data->info.host_subject)) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
 void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
 {
     int return_code;
-    int auth_flags;
-    SslVerifyCbData auth_data;
+    SPICE_SSL_VERIFY_OP auth_flags;
+    SpiceOpenSSLVerify* verify = NULL;
 
     connect_unsecure(host, options.secure_port);
     ASSERT(_ctx == NULL && _ssl == NULL && _peer != INVALID_SOCKET);
@@ -485,27 +145,23 @@ void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
 #else
         SSL_METHOD *ssl_method = TLSv1_method();
 #endif
-        auth_data.info = options.host_auth;
-        auth_data.host_name = host;
-        auth_data.all_preverify_ok = true;
-
         _ctx = SSL_CTX_new(ssl_method);
         if (_ctx == NULL) {
             ssl_error();
         }
 
-        auth_flags = auth_data.info.type_flags;
-        if ((auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_NAME) ||
-            (auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT)) {
-            std::string CA_file = auth_data.info.CA_file;
+        auth_flags = options.host_auth.type_flags;
+        if ((auth_flags & SPICE_SSL_VERIFY_OP_HOSTNAME) ||
+            (auth_flags & SPICE_SSL_VERIFY_OP_SUBJECT)) {
+            std::string CA_file = options.host_auth.CA_file;
             ASSERT(!CA_file.empty());
 
             return_code = SSL_CTX_load_verify_locations(_ctx, CA_file.c_str(), NULL);
             if (return_code != 1) {
-                if (auth_flags & RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY) {
+                if (auth_flags & SPICE_SSL_VERIFY_OP_PUBKEY) {
                     LOG_WARN("SSL_CTX_load_verify_locations failed, CA_file=%s. "
                              "only pubkey authentication is active", CA_file.c_str());
-                    auth_data.info.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY;
+                    auth_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
                 }
                 else {
                     LOG_ERROR("SSL_CTX_load_verify_locations failed CA_file=%s", CA_file.c_str());
@@ -514,10 +170,6 @@ void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
             }
         }
 
-        if (auth_flags) {
-            SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, ssl_verify_callback);
-        }
-
         return_code = SSL_CTX_set_cipher_list(_ctx, options.ciphers.c_str());
         if (return_code != 1) {
             LOG_ERROR("SSL_CTX_set_cipher_list failed, ciphers=%s", options.ciphers.c_str());
@@ -529,13 +181,19 @@ void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
             THROW("create ssl failed");
         }
 
+        verify = spice_openssl_verify_new(
+            _ssl, auth_flags,
+            host,
+            (char*)&options.host_auth.host_pubkey[0],
+            options.host_auth.host_pubkey.size(),
+            options.host_auth.host_subject.c_str());
+
         BIO* sbio = BIO_new_socket(_peer, BIO_NOCLOSE);
         if (!sbio) {
             THROW("alloc new socket bio failed");
         }
 
         SSL_set_bio(_ssl, sbio, sbio);
-        SSL_set_app_data(_ssl, &auth_data);
 
         return_code = SSL_connect(_ssl);
         if (return_code <= 0) {
@@ -546,9 +204,12 @@ void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
         }
     } catch (...) {
         Lock lock(_lock);
+        spice_openssl_verify_free(verify);
         cleanup();
         throw;
     }
+
+    spice_openssl_verify_free(verify);
 }
 
 void RedPeer::shutdown()
diff --git a/client/red_peer.h b/client/red_peer.h
index a4310e6..7e3428b 100644
--- a/client/red_peer.h
+++ b/client/red_peer.h
@@ -27,6 +27,8 @@
 #include "threads.h"
 #include "platform_utils.h"
 #include "marshaller.h"
+#include "debug.h"
+#include "ssl_verify.h"
 
 class RedPeer: protected EventSources::Socket {
 public:
@@ -41,24 +43,18 @@ public:
     class HostAuthOptions {
     public:
 
-        enum Type {
-            HOST_AUTH_OP_PUBKEY = 1,
-            HOST_AUTH_OP_NAME = (1 << 1),
-            HOST_AUTH_OP_SUBJECT = (1 << 2),
-        };
-
         typedef std::vector<uint8_t> PublicKey;
         typedef std::pair<std::string, std::string> CertFieldValuePair;
         typedef std::list<CertFieldValuePair> CertFieldValueList;
 
-        HostAuthOptions() : type_flags(0) {}
+        HostAuthOptions() : type_flags(SPICE_SSL_VERIFY_OP_NONE) {}
 
     public:
 
-        int type_flags;
+        SPICE_SSL_VERIFY_OP type_flags;
 
         PublicKey host_pubkey;
-        CertFieldValueList host_subject;
+        std::string host_subject;
         std::string CA_file;
     };
 
@@ -124,15 +120,6 @@ public:
 protected:
     virtual void on_event() {}
     virtual int get_socket() { return _peer;}
-
-    static bool x509_cert_host_name_compare(const char *cert_name, int cert_name_size,
-                                            const char *host_name);
-
-    static bool verify_pubkey(X509* cert, const HostAuthOptions::PublicKey& key);
-    static bool verify_host_name(X509* cert, const char* host_name);
-    static bool verify_subject(X509* cert, const HostAuthOptions::CertFieldValueList& subject);
-
-    static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
     void cleanup();
 
 private:
commit c16b1a924b161d8031193fc375be8e2773f8d0c1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jan 25 13:00:33 2011 +0100

    common: add ssl_verify.c common code
    
    Code adapter from RedPeer::ssl_verify_callback() and used by
    spice-gtk.
    
    Since v1:
     - fixed Makefile.am
     - added config.h include
     - autoconf alloca added in patch series
     - moved int escape inside for loop
     - added a failed case when missing assignment
     - replaced strlen () by -1
     - skip spaces after comma
     - c++ guards
    
    I didn't use bool, because openSSL uses int, and it is more future
    proof for error reporting.

diff --git a/common/Makefile.am b/common/Makefile.am
index dff9574..e0f4d49 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -34,6 +34,8 @@ libspice_common_la_SOURCES =		\
 	rop3.c				\
 	rop3.h				\
 	spice_common.h			\
+	ssl_verify.c			\
+	ssl_verify.h			\
 	$(NULL)
 
 if SUPPORT_GL
diff --git a/common/ssl_verify.c b/common/ssl_verify.c
new file mode 100644
index 0000000..5e3722e
--- /dev/null
+++ b/common/ssl_verify.c
@@ -0,0 +1,481 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "mem.h"
+#include "ssl_verify.h"
+
+#ifndef WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <ctype.h>
+
+#ifndef SPICE_DEBUG
+# define SPICE_DEBUG(format, args...)
+#endif
+
+#ifdef WIN32
+static int inet_aton(const char* ip, struct in_addr* in_addr)
+{
+    unsigned long addr = inet_addr(ip);
+
+    if (addr == INADDR_NONE) {
+        return 0;
+    }
+    in_addr->S_un.S_addr = addr;
+    return 1;
+}
+#endif
+
+static int verify_pubkey(X509* cert, const char *key, size_t key_size)
+{
+    EVP_PKEY* cert_pubkey = NULL;
+    EVP_PKEY* orig_pubkey = NULL;
+    BIO* bio = NULL;
+    int ret = 0;
+
+    if (!key || key_size == 0)
+        return 0;
+
+    if (!cert) {
+        SPICE_DEBUG("warning: no cert!");
+        return 0;
+    }
+
+    cert_pubkey = X509_get_pubkey(cert);
+    if (!cert_pubkey) {
+        SPICE_DEBUG("warning: reading public key from certificate failed");
+        goto finish;
+    }
+
+    bio = BIO_new_mem_buf((void*)key, key_size);
+    if (!bio) {
+        SPICE_DEBUG("creating BIO failed");
+        goto finish;
+    }
+
+    orig_pubkey = d2i_PUBKEY_bio(bio, NULL);
+    if (!orig_pubkey) {
+        SPICE_DEBUG("reading pubkey from bio failed");
+        goto finish;
+    }
+
+    ret = EVP_PKEY_cmp(orig_pubkey, cert_pubkey);
+
+    if (ret == 1)
+        SPICE_DEBUG("public keys match");
+    else if (ret == 0)
+        SPICE_DEBUG("public keys mismatch");
+    else
+        SPICE_DEBUG("public keys types mismatch");
+
+finish:
+    if (bio)
+        BIO_free(bio);
+
+    if (orig_pubkey)
+        EVP_PKEY_free(orig_pubkey);
+
+    if (cert_pubkey)
+        EVP_PKEY_free(cert_pubkey);
+
+    return ret;
+}
+
+/* from gnutls
+ * compare hostname against certificate, taking account of wildcards
+ * return 1 on success or 0 on error
+ *
+ * note: certnamesize is required as X509 certs can contain embedded NULs in
+ * the strings such as CN or subjectAltName
+ */
+static int _gnutls_hostname_compare(const char *certname,
+                                    size_t certnamesize, const char *hostname)
+{
+    /* find the first different character */
+    for (; *certname && *hostname && toupper (*certname) == toupper (*hostname);
+         certname++, hostname++, certnamesize--)
+        ;
+
+    /* the strings are the same */
+    if (certnamesize == 0 && *hostname == '\0')
+        return 1;
+
+    if (*certname == '*')
+        {
+            /* a wildcard certificate */
+
+            certname++;
+            certnamesize--;
+
+            while (1)
+                {
+                    /* Use a recursive call to allow multiple wildcards */
+                    if (_gnutls_hostname_compare (certname, certnamesize, hostname))
+                        return 1;
+
+                    /* wildcards are only allowed to match a single domain
+                       component or component fragment */
+                    if (*hostname == '\0' || *hostname == '.')
+                        break;
+                    hostname++;
+                }
+
+            return 0;
+        }
+
+    return 0;
+}
+
+/**
+ * From gnutls and spice red_peer.c
+ * TODO: switch to gnutls and get rid of this
+ *
+ * This function will check if the given certificate's subject matches
+ * the given hostname.  This is a basic implementation of the matching
+ * described in RFC2818 (HTTPS), which takes into account wildcards,
+ * and the DNSName/IPAddress subject alternative name PKIX extension.
+ *
+ * Returns: 1 for a successful match, and 0 on failure.
+ **/
+static int verify_hostname(X509* cert, const char *hostname)
+{
+    GENERAL_NAMES* subject_alt_names;
+    int found_dns_name = 0;
+    struct in_addr addr;
+    int addr_len = 0;
+    int cn_match = 0;
+
+    if (!cert) {
+        SPICE_DEBUG("warning: no cert!");
+        return 0;
+    }
+
+    // only IpV4 supported
+    if (inet_aton(hostname, &addr)) {
+        addr_len = sizeof(struct in_addr);
+    }
+
+    /* try matching against:
+     *  1) a DNS name as an alternative name (subjectAltName) extension
+     *     in the certificate
+     *  2) the common name (CN) in the certificate
+     *
+     *  either of these may be of the form: *.domain.tld
+     *
+     *  only try (2) if there is no subjectAltName extension of
+     *  type dNSName
+     */
+
+    /* Check through all included subjectAltName extensions, comparing
+     * against all those of type dNSName.
+     */
+    subject_alt_names = (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+    if (subject_alt_names) {
+        int num_alts = sk_GENERAL_NAME_num(subject_alt_names);
+        int i;
+        for (i = 0; i < num_alts; i++) {
+            const GENERAL_NAME* name = sk_GENERAL_NAME_value(subject_alt_names, i);
+            if (name->type == GEN_DNS) {
+                found_dns_name = 1;
+                if (_gnutls_hostname_compare((char *)ASN1_STRING_data(name->d.dNSName),
+                                             ASN1_STRING_length(name->d.dNSName),
+                                             hostname)) {
+                    SPICE_DEBUG("alt name match=%s", ASN1_STRING_data(name->d.dNSName));
+                    GENERAL_NAMES_free(subject_alt_names);
+                    return 1;
+                }
+            } else if (name->type == GEN_IPADD) {
+                int alt_ip_len = ASN1_STRING_length(name->d.iPAddress);
+                found_dns_name = 1;
+                if ((addr_len == alt_ip_len)&&
+                    !memcmp(ASN1_STRING_data(name->d.iPAddress), &addr, addr_len)) {
+                    SPICE_DEBUG("alt name IP match=%s",
+                                inet_ntoa(*((struct in_addr*)ASN1_STRING_data(name->d.dNSName))));
+                    GENERAL_NAMES_free(subject_alt_names);
+                    return 1;
+                }
+            }
+        }
+        GENERAL_NAMES_free(subject_alt_names);
+    }
+
+    if (found_dns_name) {
+        SPICE_DEBUG("warning: SubjectAltName mismatch");
+        return 0;
+    }
+
+    /* extracting commonNames */
+    X509_NAME* subject = X509_get_subject_name(cert);
+    if (subject) {
+        int pos = -1;
+        X509_NAME_ENTRY* cn_entry;
+        ASN1_STRING* cn_asn1;
+
+        while ((pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos)) != -1) {
+            cn_entry = X509_NAME_get_entry(subject, pos);
+            if (!cn_entry) {
+                continue;
+            }
+            cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
+            if (!cn_asn1) {
+                continue;
+            }
+
+            if (_gnutls_hostname_compare((char*)ASN1_STRING_data(cn_asn1),
+                                         ASN1_STRING_length(cn_asn1),
+                                         hostname)) {
+                SPICE_DEBUG("common name match=%s", (char*)ASN1_STRING_data(cn_asn1));
+                cn_match = 1;
+                break;
+            }
+        }
+    }
+
+    if (!cn_match)
+        SPICE_DEBUG("warning: common name mismatch");
+
+    return cn_match;
+}
+
+X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
+{
+    X509_NAME* in_subject;
+    const char *p;
+    char *key, *val, *k, *v = NULL;
+    enum {
+        KEY,
+        VALUE
+    } state;
+
+    key = (char*)alloca(strlen(subject));
+    val = (char*)alloca(strlen(subject));
+    in_subject = X509_NAME_new();
+
+    if (!in_subject || !key || !val) {
+        SPICE_DEBUG("failed to allocate");
+        return NULL;
+    }
+
+    *nentries = 0;
+
+    k = key;
+    state = KEY;
+    for (p = subject;; ++p) {
+        int escape = 0;
+        if (*p == '\\') {
+            ++p;
+            if (*p != '\\' && *p != ',') {
+                SPICE_DEBUG("Invalid character after \\");
+                goto fail;
+            }
+            escape = 1;
+        }
+
+        switch (state) {
+        case KEY:
+            if (*p == ' ' && k == key) {
+                continue; /* skip spaces before key */
+            } if (*p == 0) {
+                if (k == key) /* empty key, ending */
+                    goto success;
+                goto fail;
+            } else if (*p == ',' && !escape) {
+                goto fail; /* assignment is missing */
+            } else if (*p == '=' && !escape) {
+                state = VALUE;
+                *k = 0;
+                v = val;
+            } else
+                *k++ = *p;
+            break;
+        case VALUE:
+            if (*p == 0 || (*p == ',' && !escape)) {
+                if (v == val) /* empty value */
+                    goto fail;
+
+                *v = 0;
+
+                if (!X509_NAME_add_entry_by_txt(in_subject, key,
+                                                MBSTRING_UTF8,
+                                                (const unsigned char*)val,
+                                                -1, -1, 0)) {
+                    SPICE_DEBUG("warning: failed to add entry %s=%s to X509_NAME",
+                                key, val);
+                    goto fail;
+                }
+                *nentries += 1;
+
+                if (*p == 0)
+                    goto success;
+
+                state = KEY;
+                k = key;
+            } else
+                *v++ = *p;
+            break;
+        }
+    }
+
+success:
+    return in_subject;
+
+fail:
+    if (in_subject)
+        X509_NAME_free(in_subject);
+
+    return NULL;
+}
+
+int verify_subject(X509* cert, SpiceOpenSSLVerify* verify)
+{
+    X509_NAME *cert_subject = NULL;
+    int ret;
+    int in_entries;
+
+    if (!cert) {
+        SPICE_DEBUG("warning: no cert!");
+        return 0;
+    }
+
+    cert_subject = X509_get_subject_name(cert);
+    if (!cert_subject) {
+        SPICE_DEBUG("warning: reading certificate subject failed");
+        return 0;
+    }
+
+    if (!verify->in_subject) {
+        verify->in_subject = subject_to_x509_name(verify->subject, &in_entries);
+        if (!verify->in_subject) {
+            SPICE_DEBUG("warning: no in_subject!");
+            return 0;
+        }
+    }
+
+    /* Note: this check is redundant with the pre-condition in X509_NAME_cmp */
+    if (X509_NAME_entry_count(cert_subject) != in_entries) {
+        SPICE_DEBUG("subject mismatch: #entries cert=%d, input=%d",
+            X509_NAME_entry_count(cert_subject), in_entries);
+        return 0;
+    }
+
+    ret = X509_NAME_cmp(cert_subject, verify->in_subject);
+
+    if (ret == 0)
+        SPICE_DEBUG("subjects match");
+    else
+        SPICE_DEBUG("subjects mismatch");
+
+    return !ret;
+}
+
+static int openssl_verify(int preverify_ok, X509_STORE_CTX *ctx)
+{
+    int depth;
+    SpiceOpenSSLVerify *v;
+    SSL *ssl;
+    X509* cert;
+
+    ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+    v = (SpiceOpenSSLVerify*)SSL_get_app_data(ssl);
+
+    depth = X509_STORE_CTX_get_error_depth(ctx);
+    if (depth > 0) {
+        if (!preverify_ok) {
+            SPICE_DEBUG("openssl verify failed at depth=%d", depth);
+            v->all_preverify_ok = 0;
+            return 0;
+        } else
+            return 1;
+    }
+
+    /* depth == 0 */
+    cert = X509_STORE_CTX_get_current_cert(ctx);
+    if (!cert) {
+        SPICE_DEBUG("failed to get server certificate");
+        return 0;
+    }
+
+    if (v->verifyop & SPICE_SSL_VERIFY_OP_PUBKEY &&
+        verify_pubkey(cert, v->pubkey, v->pubkey_size))
+        return 1;
+
+    if (!v->all_preverify_ok || !preverify_ok)
+        return 0;
+
+    if (v->verifyop & SPICE_SSL_VERIFY_OP_HOSTNAME &&
+        verify_hostname(cert, v->hostname))
+        return 1;
+
+    if (v->verifyop & SPICE_SSL_VERIFY_OP_SUBJECT &&
+        verify_subject(cert, v))
+        return 1;
+
+    return 0;
+}
+
+SpiceOpenSSLVerify* spice_openssl_verify_new(SSL *ssl, SPICE_SSL_VERIFY_OP verifyop,
+                                             const char *hostname,
+                                             const char *pubkey, size_t pubkey_size,
+                                             const char *subject)
+{
+    SpiceOpenSSLVerify *v;
+
+    if (!verifyop)
+        return NULL;
+
+    v = spice_new0(SpiceOpenSSLVerify, 1);
+
+    v->ssl              = ssl;
+    v->verifyop         = verifyop;
+    v->hostname         = spice_strdup(hostname);
+    v->pubkey           = (char*)spice_memdup(pubkey, pubkey_size);
+    v->pubkey_size      = pubkey_size;
+    v->subject          = spice_strdup(subject);
+
+    v->all_preverify_ok = 1;
+
+    SSL_set_app_data(ssl, v);
+    SSL_set_verify(ssl,
+                   SSL_VERIFY_PEER, openssl_verify);
+
+    return v;
+}
+
+void spice_openssl_verify_free(SpiceOpenSSLVerify* verify)
+{
+    if (!verify)
+        return;
+
+    free(verify->pubkey);
+    free(verify->subject);
+    free(verify->hostname);
+
+    if (verify->in_subject)
+        X509_NAME_free(verify->in_subject);
+
+    if (verify->ssl)
+        SSL_set_app_data(verify->ssl, NULL);
+    free(verify);
+}
diff --git a/common/ssl_verify.h b/common/ssl_verify.h
new file mode 100644
index 0000000..f0b97d8
--- /dev/null
+++ b/common/ssl_verify.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSL_VERIFY_H
+#define SSL_VERIFY_H
+
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  SPICE_SSL_VERIFY_OP_NONE     = 0,
+  SPICE_SSL_VERIFY_OP_PUBKEY   = (1 << 0),
+  SPICE_SSL_VERIFY_OP_HOSTNAME = (1 << 1),
+  SPICE_SSL_VERIFY_OP_SUBJECT  = (1 << 2),
+} SPICE_SSL_VERIFY_OP;
+
+typedef struct {
+    SSL                 *ssl;
+    SPICE_SSL_VERIFY_OP verifyop;
+    int                 all_preverify_ok;
+    char                *hostname;
+    char                *pubkey;
+    size_t              pubkey_size;
+    char                *subject;
+    X509_NAME           *in_subject;
+} SpiceOpenSSLVerify;
+
+SpiceOpenSSLVerify* spice_openssl_verify_new(SSL *ssl, SPICE_SSL_VERIFY_OP verifyop,
+                                             const char *hostname,
+                                             const char *pubkey, size_t pubkey_size,
+                                             const char *subject);
+void spice_openssl_verify_free(SpiceOpenSSLVerify* verify);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+#endif // SSL_VERIFY_H
commit 2b78b4968a514262d532626d7aee78ad4a481fd2
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue May 3 13:53:59 2011 +0200

    common: add ring_get_length() for debugging purposes
    
    Please notice it has a "static" modifier, like the rest of the inlined
    functions in ring.h, so it won't warn if it isn't used.

diff --git a/common/ring.h b/common/ring.h
index ff4ef6d..bd030d2 100644
--- a/common/ring.h
+++ b/common/ring.h
@@ -154,6 +154,19 @@ static inline RingItem *ring_prev(Ring *ring, RingItem *pos)
             (var) = ring_prev(ring, var))
 
 
+static inline unsigned int ring_get_length(Ring *ring)
+{
+    RingItem *i;
+    unsigned int ret = 0;
+
+    for (i = ring_get_head(ring);
+         i != NULL;
+         i = ring_next(ring, i))
+        ret++;
+
+    return ret;
+}
+
 #ifdef __cplusplus
 }
 #endif
commit dc1d4bdb680e6fcfa078fecae5a00966b644b0a9
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue May 3 13:39:38 2011 +0200

    common: mem.h add alloca definition
    
    We don't support the autoconf ALLOCA/C_ALLOC fallback. If one day,
    someone cares for a weird platform, he can fix it.

diff --git a/common/mem.h b/common/mem.h
index 8ba6586..980ea13 100644
--- a/common/mem.h
+++ b/common/mem.h
@@ -22,10 +22,40 @@
 #include <stdlib.h>
 #include <spice/macros.h>
 
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# ifndef HAVE_ALLOCA
+#  ifdef  __cplusplus
+extern "C"
+#  endif
+void *alloca (size_t);
+# endif
+#endif
+
 typedef struct SpiceChunk {
     uint8_t *data;
     uint32_t len;
diff --git a/configure.ac b/configure.ac
index 4b3d59f..18209bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,6 +27,7 @@ AC_C_BIGENDIAN
 AC_PATH_PROGS(PYTHON, python2 python)
 
 AC_CHECK_HEADERS([sys/time.h])
+AC_FUNC_ALLOCA
 
 SPICE_LT_VERSION=m4_format("%d:%d:%d", 1, 0, 2)
 AC_SUBST(SPICE_LT_VERSION)
commit fc80f096e4b0432f873807c6d5d7a66ebb345323
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Jan 25 16:17:12 2011 +0100

    common: spice_memdup could accept NULL
    
    (this patch is not to solve a crash fix, but to align with glib API)

diff --git a/common/mem.c b/common/mem.c
index 96be351..7236cf0 100644
--- a/common/mem.c
+++ b/common/mem.c
@@ -74,6 +74,10 @@ void *spice_memdup(const void *mem, size_t n_bytes)
 {
     void *copy;
 
+    if (mem == NULL) {
+        return NULL;
+    }
+
     copy = spice_malloc(n_bytes);
     memcpy(copy, mem, n_bytes);
     return copy;
commit 27f771566d2fb76f62010acb6967a28e9045c43c
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Dec 15 18:39:14 2010 +0100

    common: add windows.h where required, make gdi_handlers static
    
    This patch has not been verified with VS/brew. It should be safe
    hopefully. Compilation is fine with mingw32/spice-gtk.

diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp
index c50d7fd..b81c2f5 100644
--- a/client/windows/platform.cpp
+++ b/client/windows/platform.cpp
@@ -45,7 +45,6 @@
 #endif
 #include <spice/vd_agent.h>
 
-int gdi_handlers = 0;
 extern HINSTANCE instance;
 
 class DefaultEventListener: public Platform::EventListener {
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 7c5f275..7a69def 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -27,6 +27,9 @@
 #include "lz.h"
 #include "region.h"
 #include "draw.h"
+#ifdef WIN32
+#include <windows.h>
+#endif
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/common/canvas_utils.c b/common/canvas_utils.c
index d861800..604f589 100644
--- a/common/canvas_utils.c
+++ b/common/canvas_utils.c
@@ -30,7 +30,7 @@
 #include "mem.h"
 
 #ifdef WIN32
-extern int gdi_handlers;
+static int gdi_handlers = 0;
 #endif
 
 #ifndef CANVAS_ERROR
diff --git a/common/canvas_utils.h b/common/canvas_utils.h
index ffed927..16ada45 100644
--- a/common/canvas_utils.h
+++ b/common/canvas_utils.h
@@ -19,6 +19,10 @@
 #ifndef _H_CANVAS_UTILS
 #define _H_CANVAS_UTILS
 
+#ifdef WIN32
+#include <windows.h>
+#endif
+
 #include <spice/types.h>
 
 #include "pixman_utils.h"
commit aae03570230d37a74b03892021e5dbe24b7739d2
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Jan 19 14:57:48 2011 +0100

    build: require c99
    
    Because we use c99: stdbool.h, inttypes.h, bool, variadic macros, // comments, ...

diff --git a/configure.ac b/configure.ac
index 430bf83..4b3d59f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,6 +14,10 @@ AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip subdir-objects])
 AM_MAINTAINER_MODE
 
 AC_PROG_CC
+AC_PROG_CC_C99
+if test x"$ac_cv_prog_cc_c99" = xno; then
+    AC_MSG_ERROR([C99 compiler is required.])
+fi
 AC_PROG_CXX
 AC_PROG_INSTALL
 AC_CANONICAL_HOST
diff --git a/server/Makefile.am b/server/Makefile.am
index 81649a4..601840a 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -16,7 +16,6 @@ INCLUDES = \
 	$(SSL_CFLAGS)				\
 	$(VISIBILITY_HIDDEN_CFLAGS)		\
 	$(WARN_CFLAGS)				\
-	-std=gnu99				\
 	$(NULL)
 
 spice_built_sources = generated_marshallers.c generated_marshallers.h generated_demarshallers.c


More information about the Spice-commits mailing list