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

#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 33 34 35

#define VIR_FROM_THIS VIR_FROM_RPC

struct _virNetSASLContext {
36
    virObjectLockable parent;
37

38 39 40 41
    const char *const*usernameWhitelist;
};

struct _virNetSASLSession {
42
    virObjectLockable parent;
43

44 45 46 47 48
    sasl_conn_t *conn;
    size_t maxbufsize;
};


49 50 51 52 53 54
static virClassPtr virNetSASLContextClass;
static virClassPtr virNetSASLSessionClass;
static void virNetSASLSessionDispose(void *obj);

static int virNetSASLContextOnceInit(void)
{
55
    if (!(virNetSASLContextClass = virClassNew(virClassForObjectLockable(),
56
                                               "virNetSASLContext",
57
                                               sizeof(virNetSASLContext),
58
                                               NULL)))
59 60
        return -1;

61
    if (!(virNetSASLSessionClass = virClassNew(virClassForObjectLockable(),
62
                                               "virNetSASLSession",
63 64 65 66 67 68 69 70 71 72
                                               sizeof(virNetSASLSession),
                                               virNetSASLSessionDispose)))
        return -1;

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContext)


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

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

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

89
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
90 91 92 93 94 95 96 97 98 99
        return NULL;

    return ctxt;
}

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

100 101 102
    if (virNetSASLContextInitialize() < 0)
        return NULL;

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

111
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
112 113
        return NULL;

114 115 116 117 118 119 120 121 122
    ctxt->usernameWhitelist = usernameWhitelist;

    return ctxt;
}

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

125
    virObjectLock(ctxt);
126 127 128

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

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

        wildcards++;
    }

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

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

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


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;

174
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
175 176
        return NULL;

177 178 179 180 181 182 183 184 185 186 187
    /* 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) {
188 189 190
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to create SASL client context: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
191 192 193 194 195 196
        goto cleanup;
    }

    return sasl;

cleanup:
197
    virObjectUnref(sasl);
198 199 200 201 202 203 204 205 206 207 208
    return NULL;
}

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

209
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
210 211
        return NULL;

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

    return sasl;

cleanup:
233
    virObjectUnref(sasl);
234 235 236 237 238 239 240
    return NULL;
}

int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
                                int ssf)
{
    int err;
241
    int ret = -1;
242
    virObjectLock(sasl);
243 244 245

    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
    if (err != SASL_OK) {
246 247 248
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set external SSF %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
249
        goto cleanup;
250
    }
251 252 253 254

    ret = 0;

cleanup:
255
    virObjectUnlock(sasl);
256
    return ret;
257 258 259 260
}

const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl)
{
261
    const void *val = NULL;
262
    int err;
263
    virObjectLock(sasl);
264 265 266

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

280
cleanup:
281
    virObjectUnlock(sasl);
282 283 284 285 286 287 288 289 290
    return (const char*)val;
}


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

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

cleanup:
304
    virObjectUnlock(sasl);
305 306 307 308 309 310 311 312 313 314
    return ssf;
}

int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
                              int minSSF,
                              int maxSSF,
                              bool allowAnonymous)
{
    sasl_security_properties_t secprops;
    int err;
315
    int ret = -1;
316 317 318 319

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

320
    virObjectLock(sasl);
321
    memset(&secprops, 0, sizeof(secprops));
322 323 324 325 326 327 328 329 330

    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) {
331 332 333
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
334
        goto cleanup;
335 336
    }

337 338 339
    ret = 0;

cleanup:
340
    virObjectUnlock(sasl);
341
    return ret;
342 343 344 345 346
}


static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl)
{
347 348 349 350
    union {
        unsigned *maxbufsize;
        const void *ptr;
    } u;
351 352
    int err;

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

    VIR_DEBUG("Negotiated bufsize is %u vs requested size %zu",
362 363
              *u.maxbufsize, sasl->maxbufsize);
    sasl->maxbufsize = *u.maxbufsize;
364 365 366 367 368 369
    return 0;
}

char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl)
{
    const char *mechlist;
370
    char *ret = NULL;
371 372
    int err;

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

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

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

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

    *clientoutlen = outlen;

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

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


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

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

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

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

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

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;
505 506 507
    int err;
    int ret = -1;

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

    *serveroutlen = outlen;

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

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


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

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

    *serveroutlen = outlen;

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

cleanup:
583
    virObjectUnlock(sasl);
584
    return ret;
585 586 587 588
}

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

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;
605
    ssize_t ret = -1;
606

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

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

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

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

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;
644
    ssize_t ret = -1;
645

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

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

cleanup:
669
    virObjectUnlock(sasl);
670
    return ret;
671 672
}

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

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

}