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

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;
1.1.1.3 ! root      247:             vs->tls.dname = g_malloc(dnameSize);
1.1       root      248:         requery:
                    249:             if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
                    250:                 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1.1.1.3 ! root      251:                     vs->tls.dname = g_realloc(vs->tls.dname, dnameSize);
1.1       root      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: 
1.1.1.3 ! root      286: #if defined(GNUTLS_VERSION_NUMBER) && \
        !           287:     GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
        !           288: 
        !           289: static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
        !           290: {
        !           291:     const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH";
        !           292:     int rc;
        !           293: 
        !           294:     rc = gnutls_priority_set_direct(s, priority, NULL);
        !           295:     if (rc != GNUTLS_E_SUCCESS) {
        !           296:         return -1;
        !           297:     }
        !           298:     return 0;
        !           299: }
        !           300: 
        !           301: #else
        !           302: 
        !           303: static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
        !           304: {
        !           305:     static const int cert_types[] = { GNUTLS_CRT_X509, 0 };
        !           306:     static const int protocols[] = {
        !           307:         GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
        !           308:     };
        !           309:     static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 };
        !           310:     static const int kx_x509[] = {
        !           311:         GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
        !           312:         GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0
        !           313:     };
        !           314:     int rc;
        !           315: 
        !           316:     rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon);
        !           317:     if (rc != GNUTLS_E_SUCCESS) {
        !           318:         return -1;
        !           319:     }
        !           320: 
        !           321:     rc = gnutls_certificate_type_set_priority(s, cert_types);
        !           322:     if (rc != GNUTLS_E_SUCCESS) {
        !           323:         return -1;
        !           324:     }
        !           325: 
        !           326:     rc = gnutls_protocol_set_priority(s, protocols);
        !           327:     if (rc != GNUTLS_E_SUCCESS) {
        !           328:         return -1;
        !           329:     }
        !           330:     return 0;
        !           331: }
        !           332: 
        !           333: #endif
1.1       root      334: 
                    335: int vnc_tls_client_setup(struct VncState *vs,
                    336:                          int needX509Creds) {
                    337: 
                    338:     VNC_DEBUG("Do TLS setup\n");
                    339:     if (vnc_tls_initialize() < 0) {
                    340:         VNC_DEBUG("Failed to init TLS\n");
                    341:         vnc_client_error(vs);
                    342:         return -1;
                    343:     }
                    344:     if (vs->tls.session == NULL) {
                    345:         if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
                    346:             vnc_client_error(vs);
                    347:             return -1;
                    348:         }
                    349: 
                    350:         if (gnutls_set_default_priority(vs->tls.session) < 0) {
                    351:             gnutls_deinit(vs->tls.session);
                    352:             vs->tls.session = NULL;
                    353:             vnc_client_error(vs);
                    354:             return -1;
                    355:         }
                    356: 
1.1.1.3 ! root      357:         if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
1.1       root      358:             gnutls_deinit(vs->tls.session);
                    359:             vs->tls.session = NULL;
                    360:             vnc_client_error(vs);
                    361:             return -1;
                    362:         }
                    363: 
                    364:         if (needX509Creds) {
                    365:             gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
                    366:             if (!x509_cred) {
                    367:                 gnutls_deinit(vs->tls.session);
                    368:                 vs->tls.session = NULL;
                    369:                 vnc_client_error(vs);
                    370:                 return -1;
                    371:             }
                    372:             if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
                    373:                 gnutls_deinit(vs->tls.session);
                    374:                 vs->tls.session = NULL;
                    375:                 gnutls_certificate_free_credentials(x509_cred);
                    376:                 vnc_client_error(vs);
                    377:                 return -1;
                    378:             }
                    379:             if (vs->vd->tls.x509verify) {
                    380:                 VNC_DEBUG("Requesting a client certificate\n");
                    381:                 gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
                    382:             }
                    383: 
                    384:         } else {
                    385:             gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
                    386:             if (!anon_cred) {
                    387:                 gnutls_deinit(vs->tls.session);
                    388:                 vs->tls.session = NULL;
                    389:                 vnc_client_error(vs);
                    390:                 return -1;
                    391:             }
                    392:             if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
                    393:                 gnutls_deinit(vs->tls.session);
                    394:                 vs->tls.session = NULL;
                    395:                 gnutls_anon_free_server_credentials(anon_cred);
                    396:                 vnc_client_error(vs);
                    397:                 return -1;
                    398:             }
                    399:         }
                    400: 
                    401:         gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
                    402:         gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
                    403:         gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
                    404:     }
                    405:     return 0;
                    406: }
                    407: 
                    408: 
                    409: void vnc_tls_client_cleanup(struct VncState *vs)
                    410: {
                    411:     if (vs->tls.session) {
                    412:         gnutls_deinit(vs->tls.session);
                    413:         vs->tls.session = NULL;
                    414:     }
                    415:     vs->tls.wiremode = VNC_WIREMODE_CLEAR;
1.1.1.3 ! root      416:     g_free(vs->tls.dname);
1.1       root      417: }
                    418: 
                    419: 
                    420: 
                    421: static int vnc_set_x509_credential(VncDisplay *vd,
                    422:                                    const char *certdir,
                    423:                                    const char *filename,
                    424:                                    char **cred,
                    425:                                    int ignoreMissing)
                    426: {
                    427:     struct stat sb;
                    428: 
                    429:     if (*cred) {
1.1.1.3 ! root      430:         g_free(*cred);
1.1       root      431:         *cred = NULL;
                    432:     }
                    433: 
1.1.1.3 ! root      434:     *cred = g_malloc(strlen(certdir) + strlen(filename) + 2);
1.1       root      435: 
                    436:     strcpy(*cred, certdir);
                    437:     strcat(*cred, "/");
                    438:     strcat(*cred, filename);
                    439: 
                    440:     VNC_DEBUG("Check %s\n", *cred);
                    441:     if (stat(*cred, &sb) < 0) {
1.1.1.3 ! root      442:         g_free(*cred);
1.1       root      443:         *cred = NULL;
                    444:         if (ignoreMissing && errno == ENOENT)
                    445:             return 0;
                    446:         return -1;
                    447:     }
                    448: 
                    449:     return 0;
                    450: }
                    451: 
                    452: 
                    453: int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
                    454:                                const char *certdir)
                    455: {
                    456:     if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
                    457:         goto cleanup;
                    458:     if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
                    459:         goto cleanup;
                    460:     if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
                    461:         goto cleanup;
                    462:     if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
                    463:         goto cleanup;
                    464: 
                    465:     return 0;
                    466: 
                    467:  cleanup:
1.1.1.3 ! root      468:     g_free(vd->tls.x509cacert);
        !           469:     g_free(vd->tls.x509cacrl);
        !           470:     g_free(vd->tls.x509cert);
        !           471:     g_free(vd->tls.x509key);
1.1       root      472:     vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
                    473:     return -1;
                    474: }
                    475: 

unix.superglobalmegacorp.com

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