virnetsaslcontext.c 19.2 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
                                               sizeof(virNetSASLSession),
                                               virNetSASLSessionDispose)))
        return -1;

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContext)

76 77 78 79 80 81 82 83
/* Apple have annotated all SASL functions as deprecated for
 * unknown reasons. Since they still work, lets just ignore
 * the warnings. If Apple finally delete the SASL functions
 * our configure check should already catch that
 */
#ifdef __APPLE__
VIR_WARNINGS_NO_DEPRECATED
#endif
84

85 86 87 88 89
virNetSASLContextPtr virNetSASLContextNewClient(void)
{
    virNetSASLContextPtr ctxt;
    int err;

90 91 92
    if (virNetSASLContextInitialize() < 0)
        return NULL;

93 94
    err = sasl_client_init(NULL);
    if (err != SASL_OK) {
95 96 97
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("failed to initialize SASL library: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
98 99 100
        return NULL;
    }

101
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
102 103 104 105 106 107 108 109 110 111
        return NULL;

    return ctxt;
}

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

112 113 114
    if (virNetSASLContextInitialize() < 0)
        return NULL;

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

123
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
124 125
        return NULL;

126 127 128 129 130 131 132 133 134
    ctxt->usernameWhitelist = usernameWhitelist;

    return ctxt;
}

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

137
    virObjectLock(ctxt);
138 139 140

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

    while (*wildcards) {
147
        int rv = fnmatch(*wildcards, identity, 0);
148 149
        if (rv == 0) {
            ret = 1;
J
Ján Tomko 已提交
150
            goto cleanup; /* Successful match */
151
        }
152
        if (rv != FNM_NOMATCH) {
153 154 155
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Malformed TLS whitelist regular expression '%s'"),
                           *wildcards);
156
            goto cleanup;
157 158 159 160 161 162
        }

        wildcards++;
    }

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

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

170
 cleanup:
171
    virObjectUnlock(ctxt);
172
    return ret;
173 174 175 176 177 178 179 180
}


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

186
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
187 188
        return NULL;

189 190 191 192 193 194 195 196 197 198 199
    /* 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) {
200 201 202
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
203 204
        goto cleanup;
    }
205
    sasl->callbacks = cbs;
206 207 208

    return sasl;

209
 cleanup:
210
    virObjectUnref(sasl);
211 212 213 214 215 216 217 218 219 220 221
    return NULL;
}

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

222
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
223 224
        return NULL;

225 226 227 228 229 230 231 232 233 234 235 236
    /* 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) {
237 238 239
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
240 241 242 243 244
        goto cleanup;
    }

    return sasl;

245
 cleanup:
246
    virObjectUnref(sasl);
247 248 249 250 251 252 253
    return NULL;
}

int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
                                int ssf)
{
    int err;
254
    int ret = -1;
255
    virObjectLock(sasl);
256 257 258

    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
    if (err != SASL_OK) {
259 260 261
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set external SSF %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
262
        goto cleanup;
263
    }
264 265 266

    ret = 0;

267
 cleanup:
268
    virObjectUnlock(sasl);
269
    return ret;
270 271 272 273
}

const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl)
{
274
    const void *val = NULL;
275
    int err;
276
    virObjectLock(sasl);
277 278 279

    err = sasl_getprop(sasl->conn, SASL_USERNAME, &val);
    if (err != SASL_OK) {
280 281 282
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL username on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
283 284
        val = NULL;
        goto cleanup;
285 286
    }
    if (val == NULL) {
287
        virReportError(VIR_ERR_AUTH_FAILED, "%s",
288
                       _("no client username was found"));
289
        goto cleanup;
290 291 292
    }
    VIR_DEBUG("SASL client username %s", (const char *)val);

293
 cleanup:
294
    virObjectUnlock(sasl);
295 296 297 298 299 300 301 302 303
    return (const char*)val;
}


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

305
    virObjectLock(sasl);
306 307
    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
    if (err != SASL_OK) {
308 309 310
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("cannot query SASL ssf on connection %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
311 312
        ssf = -1;
        goto cleanup;
313 314
    }
    ssf = *(const int *)val;
315

316
 cleanup:
317
    virObjectUnlock(sasl);
318 319 320 321 322 323 324 325 326 327
    return ssf;
}

int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
                              int minSSF,
                              int maxSSF,
                              bool allowAnonymous)
{
    sasl_security_properties_t secprops;
    int err;
328
    int ret = -1;
329 330 331 332

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

333
    virObjectLock(sasl);
334
    memset(&secprops, 0, sizeof(secprops));
335 336 337 338 339 340 341 342 343

    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) {
344 345 346
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
347
        goto cleanup;
348 349
    }

350 351
    ret = 0;

352
 cleanup:
353
    virObjectUnlock(sasl);
354
    return ret;
355 356 357 358 359
}


static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl)
{
360 361 362 363
    union {
        unsigned *maxbufsize;
        const void *ptr;
    } u;
364 365
    int err;

366
    err = sasl_getprop(sasl->conn, SASL_MAXOUTBUF, &u.ptr);
367
    if (err != SASL_OK) {
368 369 370
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot get security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
371 372 373 374
        return -1;
    }

    VIR_DEBUG("Negotiated bufsize is %u vs requested size %zu",
375 376
              *u.maxbufsize, sasl->maxbufsize);
    sasl->maxbufsize = *u.maxbufsize;
377 378 379 380 381 382
    return 0;
}

char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl)
{
    const char *mechlist;
383
    char *ret = NULL;
384 385
    int err;

386
    virObjectLock(sasl);
387 388 389 390 391 392 393 394 395
    err = sasl_listmech(sasl->conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK) {
396 397 398
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot list SASL mechanisms %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
399
        goto cleanup;
400
    }
401 402 403 404 405 406
    VIR_DEBUG("SASL mechanism list is '%s'", mechlist);
    if (STREQ(mechlist, "")) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("no SASL mechanisms are available"));
        goto cleanup;
    }
407
    ignore_value(VIR_STRDUP(ret, mechlist));
408

409
 cleanup:
410
    virObjectUnlock(sasl);
411 412 413 414 415 416 417 418 419 420 421 422
    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;
423 424
    int err;
    int ret = -1;
425 426 427 428

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

429
    virObjectLock(sasl);
430 431 432 433 434 435
    err = sasl_client_start(sasl->conn,
                            mechlist,
                            prompt_need,
                            clientout,
                            &outlen,
                            mech);
436 437 438 439 440 441

    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
442 443 444
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
445
    case SASL_CONTINUE:
446 447
        ret = VIR_NET_SASL_CONTINUE;
        break;
448
    case SASL_INTERACT:
449 450
        ret = VIR_NET_SASL_INTERACT;
        break;
451
    default:
452 453 454
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
455
        break;
456
    }
457

458
 cleanup:
459
    virObjectUnlock(sasl);
460
    return ret;
461 462 463 464 465 466 467 468 469 470 471 472
}


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;
473 474
    int err;
    int ret = -1;
475

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

479
    virObjectLock(sasl);
480 481 482 483 484 485
    err = sasl_client_step(sasl->conn,
                           serverin,
                           inlen,
                           prompt_need,
                           clientout,
                           &outlen);
486 487 488 489 490
    *clientoutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
491 492 493
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
494
    case SASL_CONTINUE:
495 496
        ret = VIR_NET_SASL_CONTINUE;
        break;
497
    case SASL_INTERACT:
498 499
        ret = VIR_NET_SASL_INTERACT;
        break;
500
    default:
501 502 503
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to step SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
504
        break;
505
    }
506

507
 cleanup:
508
    virObjectUnlock(sasl);
509
    return ret;
510 511 512 513 514 515 516 517 518 519 520
}

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;
521 522 523
    int err;
    int ret = -1;

524
    virObjectLock(sasl);
525 526 527 528 529 530
    err = sasl_server_start(sasl->conn,
                            mechname,
                            clientin,
                            inlen,
                            serverout,
                            &outlen);
531 532 533 534 535 536

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
537 538 539
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
540
    case SASL_CONTINUE:
541 542
        ret = VIR_NET_SASL_CONTINUE;
        break;
543
    case SASL_INTERACT:
544 545
        ret = VIR_NET_SASL_INTERACT;
        break;
546
    default:
547 548 549
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
550
        break;
551
    }
552

553
 cleanup:
554
    virObjectUnlock(sasl);
555
    return ret;
556 557 558 559 560 561 562 563 564 565 566
}


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

570
    virObjectLock(sasl);
571 572 573 574 575
    err = sasl_server_step(sasl->conn,
                           clientin,
                           inlen,
                           serverout,
                           &outlen);
576 577 578 579 580 581

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
582 583 584
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
585
    case SASL_CONTINUE:
586 587
        ret = VIR_NET_SASL_CONTINUE;
        break;
588
    case SASL_INTERACT:
589 590
        ret = VIR_NET_SASL_INTERACT;
        break;
591
    default:
592 593 594
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
595
        break;
596
    }
597

598
 cleanup:
599
    virObjectUnlock(sasl);
600
    return ret;
601 602 603 604
}

size_t virNetSASLSessionGetMaxBufSize(virNetSASLSessionPtr sasl)
{
605
    size_t ret;
606
    virObjectLock(sasl);
607
    ret = sasl->maxbufsize;
608
    virObjectUnlock(sasl);
609
    return ret;
610 611 612 613 614 615 616 617 618 619 620
}

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;
621
    ssize_t ret = -1;
622

623
    virObjectLock(sasl);
624 625 626 627
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
628
        goto cleanup;
629 630 631 632 633 634 635 636 637 638
    }

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

    if (err != SASL_OK) {
639 640 641
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to encode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
642
        goto cleanup;
643
    }
644 645
    ret = 0;

646
 cleanup:
647
    virObjectUnlock(sasl);
648
    return ret;
649 650 651 652 653 654 655 656 657 658 659
}

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;
660
    ssize_t ret = -1;
661

662
    virObjectLock(sasl);
663 664 665 666
    if (inputLen > sasl->maxbufsize) {
        virReportSystemError(EINVAL,
                             _("SASL data length %zu too long, max %zu"),
                             inputLen, sasl->maxbufsize);
667
        goto cleanup;
668 669 670 671 672 673 674 675 676
    }

    err = sasl_decode(sasl->conn,
                      input,
                      inlen,
                      output,
                      &outlen);
    *outputlen = outlen;
    if (err != SASL_OK) {
677 678 679
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to decode SASL data: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
680
        goto cleanup;
681
    }
682 683
    ret = 0;

684
 cleanup:
685
    virObjectUnlock(sasl);
686
    return ret;
687 688
}

689
void virNetSASLSessionDispose(void *obj)
690
{
691
    virNetSASLSessionPtr sasl = obj;
692 693 694

    if (sasl->conn)
        sasl_dispose(&sasl->conn);
695
    VIR_FREE(sasl->callbacks);
696
}
697 698 699 700

#ifdef __APPLE__
VIR_WARNINGS_RESET
#endif