Annotation of qemu/vnc-tls.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com