Annotation of qemu/ui/vnc-tls.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * QEMU VNC display driver: TLS helpers
        !             3:  *
        !             4:  * Copyright (C) 2006 Anthony Liguori <[email protected]>
        !             5:  * Copyright (C) 2006 Fabrice Bellard
        !             6:  * Copyright (C) 2009 Red Hat, Inc
        !             7:  *
        !             8:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             9:  * of this software and associated documentation files (the "Software"), to deal
        !            10:  * in the Software without restriction, including without limitation the rights
        !            11:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            12:  * copies of the Software, and to permit persons to whom the Software is
        !            13:  * furnished to do so, subject to the following conditions:
        !            14:  *
        !            15:  * The above copyright notice and this permission notice shall be included in
        !            16:  * all copies or substantial portions of the Software.
        !            17:  *
        !            18:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            19:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            20:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            21:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            22:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            23:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            24:  * THE SOFTWARE.
        !            25:  */
        !            26: 
        !            27: #include "qemu-x509.h"
        !            28: #include "vnc.h"
        !            29: #include "qemu_socket.h"
        !            30: 
        !            31: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
        !            32: /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
        !            33: static void vnc_debug_gnutls_log(int level, const char* str) {
        !            34:     VNC_DEBUG("%d %s", level, str);
        !            35: }
        !            36: #endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
        !            37: 
        !            38: 
        !            39: #define DH_BITS 1024
        !            40: static gnutls_dh_params_t dh_params;
        !            41: 
        !            42: static int vnc_tls_initialize(void)
        !            43: {
        !            44:     static int tlsinitialized = 0;
        !            45: 
        !            46:     if (tlsinitialized)
        !            47:         return 1;
        !            48: 
        !            49:     if (gnutls_global_init () < 0)
        !            50:         return 0;
        !            51: 
        !            52:     /* XXX ought to re-generate diffie-hellmen params periodically */
        !            53:     if (gnutls_dh_params_init (&dh_params) < 0)
        !            54:         return 0;
        !            55:     if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
        !            56:         return 0;
        !            57: 
        !            58: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
        !            59:     gnutls_global_set_log_level(10);
        !            60:     gnutls_global_set_log_function(vnc_debug_gnutls_log);
        !            61: #endif
        !            62: 
        !            63:     tlsinitialized = 1;
        !            64: 
        !            65:     return 1;
        !            66: }
        !            67: 
        !            68: static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
        !            69:                             const void *data,
        !            70:                             size_t len) {
        !            71:     struct VncState *vs = (struct VncState *)transport;
        !            72:     int ret;
        !            73: 
        !            74:  retry:
        !            75:     ret = send(vs->csock, data, len, 0);
        !            76:     if (ret < 0) {
        !            77:         if (errno == EINTR)
        !            78:             goto retry;
        !            79:         return -1;
        !            80:     }
        !            81:     return ret;
        !            82: }
        !            83: 
        !            84: 
        !            85: static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
        !            86:                             void *data,
        !            87:                             size_t len) {
        !            88:     struct VncState *vs = (struct VncState *)transport;
        !            89:     int ret;
        !            90: 
        !            91:  retry:
        !            92:     ret = recv(vs->csock, data, len, 0);
        !            93:     if (ret < 0) {
        !            94:         if (errno == EINTR)
        !            95:             goto retry;
        !            96:         return -1;
        !            97:     }
        !            98:     return ret;
        !            99: }
        !           100: 
        !           101: 
        !           102: static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
        !           103: {
        !           104:     gnutls_anon_server_credentials anon_cred;
        !           105:     int ret;
        !           106: 
        !           107:     if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
        !           108:         VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
        !           109:         return NULL;
        !           110:     }
        !           111: 
        !           112:     gnutls_anon_set_server_dh_params(anon_cred, dh_params);
        !           113: 
        !           114:     return anon_cred;
        !           115: }
        !           116: 
        !           117: 
        !           118: static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
        !           119: {
        !           120:     gnutls_certificate_credentials_t x509_cred;
        !           121:     int ret;
        !           122: 
        !           123:     if (!vd->tls.x509cacert) {
        !           124:         VNC_DEBUG("No CA x509 certificate specified\n");
        !           125:         return NULL;
        !           126:     }
        !           127:     if (!vd->tls.x509cert) {
        !           128:         VNC_DEBUG("No server x509 certificate specified\n");
        !           129:         return NULL;
        !           130:     }
        !           131:     if (!vd->tls.x509key) {
        !           132:         VNC_DEBUG("No server private key specified\n");
        !           133:         return NULL;
        !           134:     }
        !           135: 
        !           136:     if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
        !           137:         VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
        !           138:         return NULL;
        !           139:     }
        !           140:     if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
        !           141:                                                       vd->tls.x509cacert,
        !           142:                                                       GNUTLS_X509_FMT_PEM)) < 0) {
        !           143:         VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
        !           144:         gnutls_certificate_free_credentials(x509_cred);
        !           145:         return NULL;
        !           146:     }
        !           147: 
        !           148:     if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
        !           149:                                                      vd->tls.x509cert,
        !           150:                                                      vd->tls.x509key,
        !           151:                                                      GNUTLS_X509_FMT_PEM)) < 0) {
        !           152:         VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
        !           153:         gnutls_certificate_free_credentials(x509_cred);
        !           154:         return NULL;
        !           155:     }
        !           156: 
        !           157:     if (vd->tls.x509cacrl) {
        !           158:         if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
        !           159:                                                         vd->tls.x509cacrl,
        !           160:                                                         GNUTLS_X509_FMT_PEM)) < 0) {
        !           161:             VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
        !           162:             gnutls_certificate_free_credentials(x509_cred);
        !           163:             return NULL;
        !           164:         }
        !           165:     }
        !           166: 
        !           167:     gnutls_certificate_set_dh_params (x509_cred, dh_params);
        !           168: 
        !           169:     return x509_cred;
        !           170: }
        !           171: 
        !           172: 
        !           173: int vnc_tls_validate_certificate(struct VncState *vs)
        !           174: {
        !           175:     int ret;
        !           176:     unsigned int status;
        !           177:     const gnutls_datum_t *certs;
        !           178:     unsigned int nCerts, i;
        !           179:     time_t now;
        !           180: 
        !           181:     VNC_DEBUG("Validating client certificate\n");
        !           182:     if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
        !           183:         VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
        !           184:         return -1;
        !           185:     }
        !           186: 
        !           187:     if ((now = time(NULL)) == ((time_t)-1)) {
        !           188:         return -1;
        !           189:     }
        !           190: 
        !           191:     if (status != 0) {
        !           192:         if (status & GNUTLS_CERT_INVALID)
        !           193:             VNC_DEBUG("The certificate is not trusted.\n");
        !           194: 
        !           195:         if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
        !           196:             VNC_DEBUG("The certificate hasn't got a known issuer.\n");
        !           197: 
        !           198:         if (status & GNUTLS_CERT_REVOKED)
        !           199:             VNC_DEBUG("The certificate has been revoked.\n");
        !           200: 
        !           201:         if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
        !           202:             VNC_DEBUG("The certificate uses an insecure algorithm\n");
        !           203: 
        !           204:         return -1;
        !           205:     } else {
        !           206:         VNC_DEBUG("Certificate is valid!\n");
        !           207:     }
        !           208: 
        !           209:     /* Only support x509 for now */
        !           210:     if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
        !           211:         return -1;
        !           212: 
        !           213:     if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
        !           214:         return -1;
        !           215: 
        !           216:     for (i = 0 ; i < nCerts ; i++) {
        !           217:         gnutls_x509_crt_t cert;
        !           218:         VNC_DEBUG ("Checking certificate chain %d\n", i);
        !           219:         if (gnutls_x509_crt_init (&cert) < 0)
        !           220:             return -1;
        !           221: 
        !           222:         if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
        !           223:             gnutls_x509_crt_deinit (cert);
        !           224:             return -1;
        !           225:         }
        !           226: 
        !           227:         if (gnutls_x509_crt_get_expiration_time (cert) < now) {
        !           228:             VNC_DEBUG("The certificate has expired\n");
        !           229:             gnutls_x509_crt_deinit (cert);
        !           230:             return -1;
        !           231:         }
        !           232: 
        !           233:         if (gnutls_x509_crt_get_activation_time (cert) > now) {
        !           234:             VNC_DEBUG("The certificate is not yet activated\n");
        !           235:             gnutls_x509_crt_deinit (cert);
        !           236:             return -1;
        !           237:         }
        !           238: 
        !           239:         if (gnutls_x509_crt_get_activation_time (cert) > now) {
        !           240:             VNC_DEBUG("The certificate is not yet activated\n");
        !           241:             gnutls_x509_crt_deinit (cert);
        !           242:             return -1;
        !           243:         }
        !           244: 
        !           245:         if (i == 0) {
        !           246:             size_t dnameSize = 1024;
        !           247:             vs->tls.dname = qemu_malloc(dnameSize);
        !           248:         requery:
        !           249:             if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
        !           250:                 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
        !           251:                     vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize);
        !           252:                     goto requery;
        !           253:                 }
        !           254:                 gnutls_x509_crt_deinit (cert);
        !           255:                 VNC_DEBUG("Cannot get client distinguished name: %s",
        !           256:                           gnutls_strerror (ret));
        !           257:                 return -1;
        !           258:             }
        !           259: 
        !           260:             if (vs->vd->tls.x509verify) {
        !           261:                 int allow;
        !           262:                 if (!vs->vd->tls.acl) {
        !           263:                     VNC_DEBUG("no ACL activated, allowing access");
        !           264:                     gnutls_x509_crt_deinit (cert);
        !           265:                     continue;
        !           266:                 }
        !           267: 
        !           268:                 allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
        !           269:                                                   vs->tls.dname);
        !           270: 
        !           271:                 VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
        !           272:                           vs->tls.dname, allow ? "allowed" : "denied");
        !           273:                 if (!allow) {
        !           274:                     gnutls_x509_crt_deinit (cert);
        !           275:                     return -1;
        !           276:                 }
        !           277:             }
        !           278:         }
        !           279: 
        !           280:         gnutls_x509_crt_deinit (cert);
        !           281:     }
        !           282: 
        !           283:     return 0;
        !           284: }
        !           285: 
        !           286: 
        !           287: int vnc_tls_client_setup(struct VncState *vs,
        !           288:                          int needX509Creds) {
        !           289:     static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
        !           290:     static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
        !           291:     static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
        !           292:     static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
        !           293: 
        !           294:     VNC_DEBUG("Do TLS setup\n");
        !           295:     if (vnc_tls_initialize() < 0) {
        !           296:         VNC_DEBUG("Failed to init TLS\n");
        !           297:         vnc_client_error(vs);
        !           298:         return -1;
        !           299:     }
        !           300:     if (vs->tls.session == NULL) {
        !           301:         if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
        !           302:             vnc_client_error(vs);
        !           303:             return -1;
        !           304:         }
        !           305: 
        !           306:         if (gnutls_set_default_priority(vs->tls.session) < 0) {
        !           307:             gnutls_deinit(vs->tls.session);
        !           308:             vs->tls.session = NULL;
        !           309:             vnc_client_error(vs);
        !           310:             return -1;
        !           311:         }
        !           312: 
        !           313:         if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
        !           314:             gnutls_deinit(vs->tls.session);
        !           315:             vs->tls.session = NULL;
        !           316:             vnc_client_error(vs);
        !           317:             return -1;
        !           318:         }
        !           319: 
        !           320:         if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
        !           321:             gnutls_deinit(vs->tls.session);
        !           322:             vs->tls.session = NULL;
        !           323:             vnc_client_error(vs);
        !           324:             return -1;
        !           325:         }
        !           326: 
        !           327:         if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) {
        !           328:             gnutls_deinit(vs->tls.session);
        !           329:             vs->tls.session = NULL;
        !           330:             vnc_client_error(vs);
        !           331:             return -1;
        !           332:         }
        !           333: 
        !           334:         if (needX509Creds) {
        !           335:             gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
        !           336:             if (!x509_cred) {
        !           337:                 gnutls_deinit(vs->tls.session);
        !           338:                 vs->tls.session = NULL;
        !           339:                 vnc_client_error(vs);
        !           340:                 return -1;
        !           341:             }
        !           342:             if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
        !           343:                 gnutls_deinit(vs->tls.session);
        !           344:                 vs->tls.session = NULL;
        !           345:                 gnutls_certificate_free_credentials(x509_cred);
        !           346:                 vnc_client_error(vs);
        !           347:                 return -1;
        !           348:             }
        !           349:             if (vs->vd->tls.x509verify) {
        !           350:                 VNC_DEBUG("Requesting a client certificate\n");
        !           351:                 gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
        !           352:             }
        !           353: 
        !           354:         } else {
        !           355:             gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
        !           356:             if (!anon_cred) {
        !           357:                 gnutls_deinit(vs->tls.session);
        !           358:                 vs->tls.session = NULL;
        !           359:                 vnc_client_error(vs);
        !           360:                 return -1;
        !           361:             }
        !           362:             if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
        !           363:                 gnutls_deinit(vs->tls.session);
        !           364:                 vs->tls.session = NULL;
        !           365:                 gnutls_anon_free_server_credentials(anon_cred);
        !           366:                 vnc_client_error(vs);
        !           367:                 return -1;
        !           368:             }
        !           369:         }
        !           370: 
        !           371:         gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
        !           372:         gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
        !           373:         gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
        !           374:     }
        !           375:     return 0;
        !           376: }
        !           377: 
        !           378: 
        !           379: void vnc_tls_client_cleanup(struct VncState *vs)
        !           380: {
        !           381:     if (vs->tls.session) {
        !           382:         gnutls_deinit(vs->tls.session);
        !           383:         vs->tls.session = NULL;
        !           384:     }
        !           385:     vs->tls.wiremode = VNC_WIREMODE_CLEAR;
        !           386:     free(vs->tls.dname);
        !           387: }
        !           388: 
        !           389: 
        !           390: 
        !           391: static int vnc_set_x509_credential(VncDisplay *vd,
        !           392:                                    const char *certdir,
        !           393:                                    const char *filename,
        !           394:                                    char **cred,
        !           395:                                    int ignoreMissing)
        !           396: {
        !           397:     struct stat sb;
        !           398: 
        !           399:     if (*cred) {
        !           400:         qemu_free(*cred);
        !           401:         *cred = NULL;
        !           402:     }
        !           403: 
        !           404:     *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
        !           405: 
        !           406:     strcpy(*cred, certdir);
        !           407:     strcat(*cred, "/");
        !           408:     strcat(*cred, filename);
        !           409: 
        !           410:     VNC_DEBUG("Check %s\n", *cred);
        !           411:     if (stat(*cred, &sb) < 0) {
        !           412:         qemu_free(*cred);
        !           413:         *cred = NULL;
        !           414:         if (ignoreMissing && errno == ENOENT)
        !           415:             return 0;
        !           416:         return -1;
        !           417:     }
        !           418: 
        !           419:     return 0;
        !           420: }
        !           421: 
        !           422: 
        !           423: int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
        !           424:                                const char *certdir)
        !           425: {
        !           426:     if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
        !           427:         goto cleanup;
        !           428:     if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
        !           429:         goto cleanup;
        !           430:     if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
        !           431:         goto cleanup;
        !           432:     if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
        !           433:         goto cleanup;
        !           434: 
        !           435:     return 0;
        !           436: 
        !           437:  cleanup:
        !           438:     qemu_free(vd->tls.x509cacert);
        !           439:     qemu_free(vd->tls.x509cacrl);
        !           440:     qemu_free(vd->tls.x509cert);
        !           441:     qemu_free(vd->tls.x509key);
        !           442:     vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
        !           443:     return -1;
        !           444: }
        !           445: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.