virnetsaslcontext.c 19.6 KB
Newer Older
1 2 3
/*
 * virnetsaslcontext.c: SASL encryption/auth handling
 *
4
 * Copyright (C) 2010-2012 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26 27 28 29
 */

#include <config.h>

#include <fnmatch.h>

#include "virnetsaslcontext.h"
#include "virnetmessage.h"

#include "virterror_internal.h"
#include "memory.h"
30
#include "threads.h"
31 32 33 34 35
#include "logging.h"

#define VIR_FROM_THIS VIR_FROM_RPC

struct _virNetSASLContext {
36 37
    virObject object;

38
    virMutex lock;
39 40 41 42
    const char *const*usernameWhitelist;
};

struct _virNetSASLSession {
43 44
    virObject object;

45
    virMutex lock;
46 47 48 49 50
    sasl_conn_t *conn;
    size_t maxbufsize;
};


51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
static virClassPtr virNetSASLContextClass;
static virClassPtr virNetSASLSessionClass;
static void virNetSASLContextDispose(void *obj);
static void virNetSASLSessionDispose(void *obj);

static int virNetSASLContextOnceInit(void)
{
    if (!(virNetSASLContextClass = virClassNew("virNetSASLContext",
                                               sizeof(virNetSASLContext),
                                               virNetSASLContextDispose)))
        return -1;

    if (!(virNetSASLSessionClass = virClassNew("virNetSASLSession",
                                               sizeof(virNetSASLSession),
                                               virNetSASLSessionDispose)))
        return -1;

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContext)


74 75 76 77 78
virNetSASLContextPtr virNetSASLContextNewClient(void)
{
    virNetSASLContextPtr ctxt;
    int err;

79 80 81
    if (virNetSASLContextInitialize() < 0)
        return NULL;

82 83
    err = sasl_client_init(NULL);
    if (err != SASL_OK) {
84 85 86
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("failed to initialize SASL library: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
87 88 89
        return NULL;
    }

90
    if (!(ctxt = virObjectNew(virNetSASLContextClass)))
91 92
        return NULL;

93
    if (virMutexInit(&ctxt->lock) < 0) {
94 95
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to initialized mutex"));
96 97 98 99
        VIR_FREE(ctxt);
        return NULL;
    }

100 101 102 103 104 105 106 107
    return ctxt;
}

virNetSASLContextPtr virNetSASLContextNewServer(const char *const*usernameWhitelist)
{
    virNetSASLContextPtr ctxt;
    int err;

108 109 110
    if (virNetSASLContextInitialize() < 0)
        return NULL;

111 112
    err = sasl_server_init(NULL, "libvirt");
    if (err != SASL_OK) {
113 114 115
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("failed to initialize SASL library: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
116 117 118
        return NULL;
    }

119
    if (!(ctxt = virObjectNew(virNetSASLContextClass)))
120 121
        return NULL;

122
    if (virMutexInit(&ctxt->lock) < 0) {
123 124
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to initialized mutex"));
125 126 127 128
        VIR_FREE(ctxt);
        return NULL;
    }

129 130 131 132 133 134 135 136 137
    ctxt->usernameWhitelist = usernameWhitelist;

    return ctxt;
}

int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt,
                                   const char *identity)
{
    const char *const*wildcards;
138 139 140
    int ret = -1;

    virMutexLock(&ctxt->lock);
141 142 143

    /* If the list is not set, allow any DN. */
    wildcards = ctxt->usernameWhitelist;
144 145 146 147
    if (!wildcards) {
        ret = 1; /* No ACL, allow all */
        goto cleanup;
    }
148 149

    while (*wildcards) {
150 151 152 153 154
        int rv = fnmatch (*wildcards, identity, 0);
        if (rv == 0) {
            ret = 1;
            goto cleanup; /* Succesful match */
        }
155
        if (rv != FNM_NOMATCH) {
156 157 158
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Malformed TLS whitelist regular expression '%s'"),
                           *wildcards);
159
            goto cleanup;
160 161 162 163 164 165 166 167 168
        }

        wildcards++;
    }

    /* Denied */
    VIR_ERROR(_("SASL client %s not allowed in whitelist"), identity);

    /* This is the most common error: make it informative. */
169 170
    virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
                   _("Client's username is not on the list of allowed clients"));
171 172 173 174 175
    ret = 0;

cleanup:
    virMutexUnlock(&ctxt->lock);
    return ret;
176 177 178
}


179
void virNetSASLContextDispose(void *obj)
180
{
181
    virNetSASLContextPtr ctxt = obj;
182

183
    virMutexDestroy(&ctxt->lock);
184 185 186 187 188 189 190 191 192 193 194 195
}

virNetSASLSessionPtr virNetSASLSessionNewClient(virNetSASLContextPtr ctxt ATTRIBUTE_UNUSED,
                                                const char *service,
                                                const char *hostname,
                                                const char *localAddr,
                                                const char *remoteAddr,
                                                const sasl_callback_t *cbs)
{
    virNetSASLSessionPtr sasl = NULL;
    int err;

196 197
    if (!(sasl = virObjectNew(virNetSASLSessionClass)))
        return NULL;
198

199
    if (virMutexInit(&sasl->lock) < 0) {
200 201
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to initialized mutex"));
202 203 204 205
        VIR_FREE(sasl);
        return NULL;
    }

206 207 208 209 210 211 212 213 214 215 216
    /* Arbitrary size for amount of data we can encode in a single block */
    sasl->maxbufsize = 1 << 16;

    err = sasl_client_new(service,
                          hostname,
                          localAddr,
                          remoteAddr,
                          cbs,
                          SASL_SUCCESS_DATA,
                          &sasl->conn);
    if (err != SASL_OK) {
217 218 219
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
220 221 222 223 224 225
        goto cleanup;
    }

    return sasl;

cleanup:
226
    virObjectUnref(sasl);
227 228 229 230 231 232 233 234 235 236 237
    return NULL;
}

virNetSASLSessionPtr virNetSASLSessionNewServer(virNetSASLContextPtr ctxt ATTRIBUTE_UNUSED,
                                                const char *service,
                                                const char *localAddr,
                                                const char *remoteAddr)
{
    virNetSASLSessionPtr sasl = NULL;
    int err;

238 239
    if (!(sasl = virObjectNew(virNetSASLSessionClass)))
        return NULL;
240

241
    if (virMutexInit(&sasl->lock) < 0) {
242 243
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to initialized mutex"));
244 245 246 247
        VIR_FREE(sasl);
        return NULL;
    }

248 249 250 251 252 253 254 255 256 257 258 259
    /* Arbitrary size for amount of data we can encode in a single block */
    sasl->maxbufsize = 1 << 16;

    err = sasl_server_new(service,
                          NULL,
                          NULL,
                          localAddr,
                          remoteAddr,
                          NULL,
                          SASL_SUCCESS_DATA,
                          &sasl->conn);
    if (err != SASL_OK) {
260 261 262
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
263 264 265 266 267 268
        goto cleanup;
    }

    return sasl;

cleanup:
269
    virObjectUnref(sasl);
270 271 272 273 274 275 276
    return NULL;
}

int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
                                int ssf)
{
    int err;
277 278
    int ret = -1;
    virMutexLock(&sasl->lock);
279 280 281

    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
    if (err != SASL_OK) {
282 283 284
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set external SSF %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
285
        goto cleanup;
286
    }
287 288 289 290

    ret = 0;

cleanup:
291
    virMutexUnlock(&sasl->lock);
292
    return ret;
293 294 295 296
}

const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl)
{
297
    const void *val = NULL;
298
    int err;
299
    virMutexLock(&sasl->lock);
300 301 302

    err = sasl_getprop(sasl->conn, SASL_USERNAME, &val);
    if (err != SASL_OK) {
303 304 305
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL username on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
306 307
        val = NULL;
        goto cleanup;
308 309
    }
    if (val == NULL) {
310
        virReportError(VIR_ERR_AUTH_FAILED, "%s",
311
                       _("no client username was found"));
312
        goto cleanup;
313 314 315
    }
    VIR_DEBUG("SASL client username %s", (const char *)val);

316 317
cleanup:
    virMutexUnlock(&sasl->lock);
318 319 320 321 322 323 324 325 326
    return (const char*)val;
}


int virNetSASLSessionGetKeySize(virNetSASLSessionPtr sasl)
{
    int err;
    int ssf;
    const void *val;
327 328

    virMutexLock(&sasl->lock);
329 330
    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
    if (err != SASL_OK) {
331 332 333
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL ssf on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
334 335
        ssf = -1;
        goto cleanup;
336 337
    }
    ssf = *(const int *)val;
338 339 340

cleanup:
    virMutexUnlock(&sasl->lock);
341 342 343 344 345 346 347 348 349 350
    return ssf;
}

int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
                              int minSSF,
                              int maxSSF,
                              bool allowAnonymous)
{
    sasl_security_properties_t secprops;
    int err;
351
    int ret = -1;
352 353 354 355

    VIR_DEBUG("minSSF=%d maxSSF=%d allowAnonymous=%d maxbufsize=%zu",
              minSSF, maxSSF, allowAnonymous, sasl->maxbufsize);

356
    virMutexLock(&sasl->lock);
357
    memset(&secprops, 0, sizeof(secprops));
358 359 360 361 362 363 364 365 366

    secprops.min_ssf = minSSF;
    secprops.max_ssf = maxSSF;
    secprops.maxbufsize = sasl->maxbufsize;
    secprops.security_flags = allowAnonymous ? 0 :
        SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;

    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
    if (err != SASL_OK) {
367 368 369
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
370
        goto cleanup;
371 372
    }

373 374 375 376 377
    ret = 0;

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
378 379 380 381 382
}


static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl)
{
383 384 385 386
    union {
        unsigned *maxbufsize;
        const void *ptr;
    } u;
387 388
    int err;

389
    err = sasl_getprop(sasl->conn, SASL_MAXOUTBUF, &u.ptr);
390
    if (err != SASL_OK) {
391 392 393
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot get security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
394 395 396 397
        return -1;
    }

    VIR_DEBUG("Negotiated bufsize is %u vs requested size %zu",
398 399
              *u.maxbufsize, sasl->maxbufsize);
    sasl->maxbufsize = *u.maxbufsize;
400 401 402 403 404 405
    return 0;
}

char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl)
{
    const char *mechlist;
406
    char *ret = NULL;
407 408
    int err;

409
    virMutexLock(&sasl->lock);
410 411 412 413 414 415 416 417 418
    err = sasl_listmech(sasl->conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK) {
419 420 421
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot list SASL mechanisms %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
422
        goto cleanup;
423 424 425
    }
    if (!(ret = strdup(mechlist))) {
        virReportOOMError();
426
        goto cleanup;
427
    }
428 429 430

cleanup:
    virMutexUnlock(&sasl->lock);
431 432 433 434 435 436 437 438 439 440 441 442
    return ret;
}


int virNetSASLSessionClientStart(virNetSASLSessionPtr sasl,
                                 const char *mechlist,
                                 sasl_interact_t **prompt_need,
                                 const char **clientout,
                                 size_t *clientoutlen,
                                 const char **mech)
{
    unsigned outlen = 0;
443 444
    int err;
    int ret = -1;
445 446 447 448

    VIR_DEBUG("sasl=%p mechlist=%s prompt_need=%p clientout=%p clientoutlen=%p mech=%p",
              sasl, mechlist, prompt_need, clientout, clientoutlen, mech);

449 450 451 452 453 454 455
    virMutexLock(&sasl->lock);
    err = sasl_client_start(sasl->conn,
                            mechlist,
                            prompt_need,
                            clientout,
                            &outlen,
                            mech);
456 457 458 459 460 461

    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
462 463 464
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
465
    case SASL_CONTINUE:
466 467
        ret = VIR_NET_SASL_CONTINUE;
        break;
468
    case SASL_INTERACT:
469 470
        ret = VIR_NET_SASL_INTERACT;
        break;
471
    default:
472 473 474
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
475
        break;
476
    }
477 478 479 480

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
481 482 483 484 485 486 487 488 489 490 491 492
}


int virNetSASLSessionClientStep(virNetSASLSessionPtr sasl,
                                const char *serverin,
                                size_t serverinlen,
                                sasl_interact_t **prompt_need,
                                const char **clientout,
                                size_t *clientoutlen)
{
    unsigned inlen = serverinlen;
    unsigned outlen = 0;
493 494
    int err;
    int ret = -1;
495 496 497 498

    VIR_DEBUG("sasl=%p serverin=%s serverinlen=%zu prompt_need=%p clientout=%p clientoutlen=%p",
              sasl, serverin, serverinlen, prompt_need, clientout, clientoutlen);

499 500 501 502 503 504 505
    virMutexLock(&sasl->lock);
    err = sasl_client_step(sasl->conn,
                           serverin,
                           inlen,
                           prompt_need,
                           clientout,
                           &outlen);
506 507 508 509 510
    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
511 512 513
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
514
    case SASL_CONTINUE:
515 516
        ret = VIR_NET_SASL_CONTINUE;
        break;
517
    case SASL_INTERACT:
518 519
        ret = VIR_NET_SASL_INTERACT;
        break;
520
    default:
521 522 523
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to step SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
524
        break;
525
    }
526 527 528 529

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
530 531 532 533 534 535 536 537 538 539 540
}

int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl,
                                 const char *mechname,
                                 const char *clientin,
                                 size_t clientinlen,
                                 const char **serverout,
                                 size_t *serveroutlen)
{
    unsigned inlen = clientinlen;
    unsigned outlen = 0;
541 542 543 544 545 546 547 548 549 550
    int err;
    int ret = -1;

    virMutexLock(&sasl->lock);
    err = sasl_server_start(sasl->conn,
                            mechname,
                            clientin,
                            inlen,
                            serverout,
                            &outlen);
551 552 553 554 555 556

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
557 558 559
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
560
    case SASL_CONTINUE:
561 562
        ret = VIR_NET_SASL_CONTINUE;
        break;
563
    case SASL_INTERACT:
564 565
        ret = VIR_NET_SASL_INTERACT;
        break;
566
    default:
567 568 569
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
570
        break;
571
    }
572 573 574 575

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
576 577 578 579 580 581 582 583 584 585 586
}


int virNetSASLSessionServerStep(virNetSASLSessionPtr sasl,
                                const char *clientin,
                                size_t clientinlen,
                                const char **serverout,
                                size_t *serveroutlen)
{
    unsigned inlen = clientinlen;
    unsigned outlen = 0;
587 588
    int err;
    int ret = -1;
589

590 591 592 593 594 595
    virMutexLock(&sasl->lock);
    err = sasl_server_step(sasl->conn,
                           clientin,
                           inlen,
                           serverout,
                           &outlen);
596 597 598 599 600 601

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
602 603 604
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
605
    case SASL_CONTINUE:
606 607
        ret = VIR_NET_SASL_CONTINUE;
        break;
608
    case SASL_INTERACT:
609 610
        ret = VIR_NET_SASL_INTERACT;
        break;
611
    default:
612 613 614
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
615
        break;
616
    }
617 618 619 620

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
621 622 623 624
}

size_t virNetSASLSessionGetMaxBufSize(virNetSASLSessionPtr sasl)
{
625 626 627 628 629
    size_t ret;
    virMutexLock(&sasl->lock);
    ret = sasl->maxbufsize;
    virMutexUnlock(&sasl->lock);
    return ret;
630 631 632 633 634 635 636 637 638 639 640
}

ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl,
                                const char *input,
                                size_t inputLen,
                                const char **output,
                                size_t *outputlen)
{
    unsigned inlen = inputLen;
    unsigned outlen = 0;
    int err;
641
    ssize_t ret = -1;
642

643
    virMutexLock(&sasl->lock);
644 645 646 647
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
648
        goto cleanup;
649 650 651 652 653 654 655 656 657 658
    }

    err = sasl_encode(sasl->conn,
                      input,
                      inlen,
                      output,
                      &outlen);
    *outputlen = outlen;

    if (err != SASL_OK) {
659 660 661
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to encode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
662
        goto cleanup;
663
    }
664 665 666 667 668
    ret = 0;

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
669 670 671 672 673 674 675 676 677 678 679
}

ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl,
                                const char *input,
                                size_t inputLen,
                                const char **output,
                                size_t *outputlen)
{
    unsigned inlen = inputLen;
    unsigned outlen = 0;
    int err;
680
    ssize_t ret = -1;
681

682
    virMutexLock(&sasl->lock);
683 684 685 686
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
687
        goto cleanup;
688 689 690 691 692 693 694 695 696
    }

    err = sasl_decode(sasl->conn,
                      input,
                      inlen,
                      output,
                      &outlen);
    *outputlen = outlen;
    if (err != SASL_OK) {
697 698 699
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to decode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
700
        goto cleanup;
701
    }
702 703 704 705 706
    ret = 0;

cleanup:
    virMutexUnlock(&sasl->lock);
    return ret;
707 708
}

709
void virNetSASLSessionDispose(void *obj)
710
{
711
    virNetSASLSessionPtr sasl = obj;
712 713 714 715

    if (sasl->conn)
        sasl_dispose(&sasl->conn);

716
    virMutexDestroy(&sasl->lock);
717
}