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

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:
1.1.1.2 ! root       92:     ret = qemu_recv(vs->csock, data, len, 0);
1.1       root       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.