virnetsaslcontext.c 18.7 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
 */

#include <config.h>

#include <fnmatch.h>

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

28
#include "virerror.h"
29
#include "viralloc.h"
30
#include "virthread.h"
31
#include "virlog.h"
32
#include "virstring.h"
33 34 35

#define VIR_FROM_THIS VIR_FROM_RPC

36 37
VIR_LOG_INIT("rpc.netsaslcontext");

38
struct _virNetSASLContext {
39
    virObjectLockable parent;
40

41 42 43 44
    const char *const*usernameWhitelist;
};

struct _virNetSASLSession {
45
    virObjectLockable parent;
46

47 48
    sasl_conn_t *conn;
    size_t maxbufsize;
49
    sasl_callback_t *callbacks;
50 51 52
};


53 54 55 56 57 58
static virClassPtr virNetSASLContextClass;
static virClassPtr virNetSASLSessionClass;
static void virNetSASLSessionDispose(void *obj);

static int virNetSASLContextOnceInit(void)
{
59
    if (!(virNetSASLContextClass = virClassNew(virClassForObjectLockable(),
60
                                               "virNetSASLContext",
61
                                               sizeof(virNetSASLContext),
62
                                               NULL)))
63 64
        return -1;

65
    if (!(virNetSASLSessionClass = virClassNew(virClassForObjectLockable(),
66
                                               "virNetSASLSession",
67 68 69 70 71 72 73 74 75 76
                                               sizeof(virNetSASLSession),
                                               virNetSASLSessionDispose)))
        return -1;

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContext)


77 78 79 80 81
virNetSASLContextPtr virNetSASLContextNewClient(void)
{
    virNetSASLContextPtr ctxt;
    int err;

82 83 84
    if (virNetSASLContextInitialize() < 0)
        return NULL;

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

93
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
94 95 96 97 98 99 100 101 102 103
        return NULL;

    return ctxt;
}

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

104 105 106
    if (virNetSASLContextInitialize() < 0)
        return NULL;

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

115
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
116 117
        return NULL;

118 119 120 121 122 123 124 125 126
    ctxt->usernameWhitelist = usernameWhitelist;

    return ctxt;
}

int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt,
                                   const char *identity)
{
    const char *const*wildcards;
127 128
    int ret = -1;

129
    virObjectLock(ctxt);
130 131 132

    /* If the list is not set, allow any DN. */
    wildcards = ctxt->usernameWhitelist;
133 134 135 136
    if (!wildcards) {
        ret = 1; /* No ACL, allow all */
        goto cleanup;
    }
137 138

    while (*wildcards) {
139
        int rv = fnmatch(*wildcards, identity, 0);
140 141
        if (rv == 0) {
            ret = 1;
J
Ján Tomko 已提交
142
            goto cleanup; /* Successful match */
143
        }
144
        if (rv != FNM_NOMATCH) {
145 146 147
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Malformed TLS whitelist regular expression '%s'"),
                           *wildcards);
148
            goto cleanup;
149 150 151 152 153 154
        }

        wildcards++;
    }

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

    /* This is the most common error: make it informative. */
158 159
    virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
                   _("Client's username is not on the list of allowed clients"));
160 161 162
    ret = 0;

cleanup:
163
    virObjectUnlock(ctxt);
164
    return ret;
165 166 167 168 169 170 171 172
}


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

178
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
179 180
        return NULL;

181 182 183 184 185 186 187 188 189 190 191
    /* 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) {
192 193 194
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
195 196
        goto cleanup;
    }
197
    sasl->callbacks = cbs;
198 199 200 201

    return sasl;

cleanup:
202
    virObjectUnref(sasl);
203 204 205 206 207 208 209 210 211 212 213
    return NULL;
}

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

214
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
215 216
        return NULL;

217 218 219 220 221 222 223 224 225 226 227 228
    /* 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) {
229 230 231
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
232 233 234 235 236 237
        goto cleanup;
    }

    return sasl;

cleanup:
238
    virObjectUnref(sasl);
239 240 241 242 243 244 245
    return NULL;
}

int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
                                int ssf)
{
    int err;
246
    int ret = -1;
247
    virObjectLock(sasl);
248 249 250

    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
    if (err != SASL_OK) {
251 252 253
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set external SSF %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
254
        goto cleanup;
255
    }
256 257 258 259

    ret = 0;

cleanup:
260
    virObjectUnlock(sasl);
261
    return ret;
262 263 264 265
}

const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl)
{
266
    const void *val = NULL;
267
    int err;
268
    virObjectLock(sasl);
269 270 271

    err = sasl_getprop(sasl->conn, SASL_USERNAME, &val);
    if (err != SASL_OK) {
272 273 274
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL username on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
275 276
        val = NULL;
        goto cleanup;
277 278
    }
    if (val == NULL) {
279
        virReportError(VIR_ERR_AUTH_FAILED, "%s",
280
                       _("no client username was found"));
281
        goto cleanup;
282 283 284
    }
    VIR_DEBUG("SASL client username %s", (const char *)val);

285
cleanup:
286
    virObjectUnlock(sasl);
287 288 289 290 291 292 293 294 295
    return (const char*)val;
}


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

297
    virObjectLock(sasl);
298 299
    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
    if (err != SASL_OK) {
300 301 302
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL ssf on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
303 304
        ssf = -1;
        goto cleanup;
305 306
    }
    ssf = *(const int *)val;
307 308

cleanup:
309
    virObjectUnlock(sasl);
310 311 312 313 314 315 316 317 318 319
    return ssf;
}

int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
                              int minSSF,
                              int maxSSF,
                              bool allowAnonymous)
{
    sasl_security_properties_t secprops;
    int err;
320
    int ret = -1;
321 322 323 324

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

325
    virObjectLock(sasl);
326
    memset(&secprops, 0, sizeof(secprops));
327 328 329 330 331 332 333 334 335

    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) {
336 337 338
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
339
        goto cleanup;
340 341
    }

342 343 344
    ret = 0;

cleanup:
345
    virObjectUnlock(sasl);
346
    return ret;
347 348 349 350 351
}


static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl)
{
352 353 354 355
    union {
        unsigned *maxbufsize;
        const void *ptr;
    } u;
356 357
    int err;

358
    err = sasl_getprop(sasl->conn, SASL_MAXOUTBUF, &u.ptr);
359
    if (err != SASL_OK) {
360 361 362
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot get security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
363 364 365 366
        return -1;
    }

    VIR_DEBUG("Negotiated bufsize is %u vs requested size %zu",
367 368
              *u.maxbufsize, sasl->maxbufsize);
    sasl->maxbufsize = *u.maxbufsize;
369 370 371 372 373 374
    return 0;
}

char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl)
{
    const char *mechlist;
375
    char *ret = NULL;
376 377
    int err;

378
    virObjectLock(sasl);
379 380 381 382 383 384 385 386 387
    err = sasl_listmech(sasl->conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK) {
388 389 390
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot list SASL mechanisms %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
391
        goto cleanup;
392
    }
393
    ignore_value(VIR_STRDUP(ret, mechlist));
394 395

cleanup:
396
    virObjectUnlock(sasl);
397 398 399 400 401 402 403 404 405 406 407 408
    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;
409 410
    int err;
    int ret = -1;
411 412 413 414

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

415
    virObjectLock(sasl);
416 417 418 419 420 421
    err = sasl_client_start(sasl->conn,
                            mechlist,
                            prompt_need,
                            clientout,
                            &outlen,
                            mech);
422 423 424 425 426 427

    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
428 429 430
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
431
    case SASL_CONTINUE:
432 433
        ret = VIR_NET_SASL_CONTINUE;
        break;
434
    case SASL_INTERACT:
435 436
        ret = VIR_NET_SASL_INTERACT;
        break;
437
    default:
438 439 440
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
441
        break;
442
    }
443 444

cleanup:
445
    virObjectUnlock(sasl);
446
    return ret;
447 448 449 450 451 452 453 454 455 456 457 458
}


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;
459 460
    int err;
    int ret = -1;
461

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

465
    virObjectLock(sasl);
466 467 468 469 470 471
    err = sasl_client_step(sasl->conn,
                           serverin,
                           inlen,
                           prompt_need,
                           clientout,
                           &outlen);
472 473 474 475 476
    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
477 478 479
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
480
    case SASL_CONTINUE:
481 482
        ret = VIR_NET_SASL_CONTINUE;
        break;
483
    case SASL_INTERACT:
484 485
        ret = VIR_NET_SASL_INTERACT;
        break;
486
    default:
487 488 489
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to step SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
490
        break;
491
    }
492 493

cleanup:
494
    virObjectUnlock(sasl);
495
    return ret;
496 497 498 499 500 501 502 503 504 505 506
}

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;
507 508 509
    int err;
    int ret = -1;

510
    virObjectLock(sasl);
511 512 513 514 515 516
    err = sasl_server_start(sasl->conn,
                            mechname,
                            clientin,
                            inlen,
                            serverout,
                            &outlen);
517 518 519 520 521 522

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
523 524 525
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
526
    case SASL_CONTINUE:
527 528
        ret = VIR_NET_SASL_CONTINUE;
        break;
529
    case SASL_INTERACT:
530 531
        ret = VIR_NET_SASL_INTERACT;
        break;
532
    default:
533 534 535
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
536
        break;
537
    }
538 539

cleanup:
540
    virObjectUnlock(sasl);
541
    return ret;
542 543 544 545 546 547 548 549 550 551 552
}


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

556
    virObjectLock(sasl);
557 558 559 560 561
    err = sasl_server_step(sasl->conn,
                           clientin,
                           inlen,
                           serverout,
                           &outlen);
562 563 564 565 566 567

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
568 569 570
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
571
    case SASL_CONTINUE:
572 573
        ret = VIR_NET_SASL_CONTINUE;
        break;
574
    case SASL_INTERACT:
575 576
        ret = VIR_NET_SASL_INTERACT;
        break;
577
    default:
578 579 580
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
581
        break;
582
    }
583 584

cleanup:
585
    virObjectUnlock(sasl);
586
    return ret;
587 588 589 590
}

size_t virNetSASLSessionGetMaxBufSize(virNetSASLSessionPtr sasl)
{
591
    size_t ret;
592
    virObjectLock(sasl);
593
    ret = sasl->maxbufsize;
594
    virObjectUnlock(sasl);
595
    return ret;
596 597 598 599 600 601 602 603 604 605 606
}

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;
607
    ssize_t ret = -1;
608

609
    virObjectLock(sasl);
610 611 612 613
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
614
        goto cleanup;
615 616 617 618 619 620 621 622 623 624
    }

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

    if (err != SASL_OK) {
625 626 627
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to encode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
628
        goto cleanup;
629
    }
630 631 632
    ret = 0;

cleanup:
633
    virObjectUnlock(sasl);
634
    return ret;
635 636 637 638 639 640 641 642 643 644 645
}

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;
646
    ssize_t ret = -1;
647

648
    virObjectLock(sasl);
649 650 651 652
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
653
        goto cleanup;
654 655 656 657 658 659 660 661 662
    }

    err = sasl_decode(sasl->conn,
                      input,
                      inlen,
                      output,
                      &outlen);
    *outputlen = outlen;
    if (err != SASL_OK) {
663 664 665
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to decode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
666
        goto cleanup;
667
    }
668 669 670
    ret = 0;

cleanup:
671
    virObjectUnlock(sasl);
672
    return ret;
673 674
}

675
void virNetSASLSessionDispose(void *obj)
676
{
677
    virNetSASLSessionPtr sasl = obj;
678 679 680

    if (sasl->conn)
        sasl_dispose(&sasl->conn);
681
    VIR_FREE(sasl->callbacks);
682
}