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

#include <config.h>

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

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

#define VIR_FROM_THIS VIR_FROM_RPC

34 35
VIR_LOG_INIT("rpc.netsaslcontext");

36
struct _virNetSASLContext {
37
    virObjectLockable parent;
38

39
    const char *const *usernameACL;
40 41 42
};

struct _virNetSASLSession {
43
    virObjectLockable parent;
44

45 46
    sasl_conn_t *conn;
    size_t maxbufsize;
47
    sasl_callback_t *callbacks;
48 49 50
};


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

static int virNetSASLContextOnceInit(void)
{
58
    if (!VIR_CLASS_NEW(virNetSASLContext, virClassForObjectLockable()))
59 60
        return -1;

61
    if (!VIR_CLASS_NEW(virNetSASLSession, virClassForObjectLockable()))
62 63 64 65 66
        return -1;

    return 0;
}

67
VIR_ONCE_GLOBAL_INIT(virNetSASLContext);
68

69 70 71 72 73 74 75 76
/* 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
77

78
static int virNetSASLContextClientOnceInit(void)
79
{
80 81 82 83 84 85 86 87 88 89 90 91
    int err = sasl_client_init(NULL);
    if (err != SASL_OK) {
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("failed to initialize SASL library: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
        return -1;
    }

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContextClient);
92

93

94 95 96
static int virNetSASLContextServerOnceInit(void)
{
    int err = sasl_server_init(NULL, "libvirt");
97
    if (err != SASL_OK) {
98 99 100
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("failed to initialize SASL library: %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
101
        return -1;
102 103
    }

104 105 106 107 108 109 110 111 112 113 114 115 116 117
    return 0;
}

VIR_ONCE_GLOBAL_INIT(virNetSASLContextServer);


virNetSASLContextPtr virNetSASLContextNewClient(void)
{
    virNetSASLContextPtr ctxt;

    if (virNetSASLContextInitialize() < 0 ||
        virNetSASLContextClientInitialize() < 0)
        return NULL;

118
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
119 120 121 122 123
        return NULL;

    return ctxt;
}

124
virNetSASLContextPtr virNetSASLContextNewServer(const char *const *usernameACL)
125 126
{
    virNetSASLContextPtr ctxt;
127

128 129
    if (virNetSASLContextInitialize() < 0 ||
        virNetSASLContextServerInitialize() < 0)
130 131
        return NULL;

132
    if (!(ctxt = virObjectLockableNew(virNetSASLContextClass)))
133 134
        return NULL;

135
    ctxt->usernameACL = usernameACL;
136 137 138 139 140 141 142 143

    return ctxt;
}

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

146
    virObjectLock(ctxt);
147 148

    /* If the list is not set, allow any DN. */
149
    wildcards = ctxt->usernameACL;
150 151 152 153
    if (!wildcards) {
        ret = 1; /* No ACL, allow all */
        goto cleanup;
    }
154 155

    while (*wildcards) {
156
        if (g_pattern_match_simple(*wildcards, identity)) {
157
            ret = 1;
J
Ján Tomko 已提交
158
            goto cleanup; /* Successful match */
159
        }
160 161 162 163 164

        wildcards++;
    }

    /* Denied */
165
    VIR_ERROR(_("SASL client identity '%s' not allowed by ACL"), identity);
166 167

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

172
 cleanup:
173
    virObjectUnlock(ctxt);
174
    return ret;
175 176 177
}


J
Ján Tomko 已提交
178
virNetSASLSessionPtr virNetSASLSessionNewClient(virNetSASLContextPtr ctxt G_GNUC_UNUSED,
179 180 181 182
                                                const char *service,
                                                const char *hostname,
                                                const char *localAddr,
                                                const char *remoteAddr,
183
                                                sasl_callback_t *cbs)
184 185 186 187
{
    virNetSASLSessionPtr sasl = NULL;
    int err;

188
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
189 190
        return NULL;

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

    return sasl;

211
 cleanup:
212
    virObjectUnref(sasl);
213 214 215
    return NULL;
}

J
Ján Tomko 已提交
216
virNetSASLSessionPtr virNetSASLSessionNewServer(virNetSASLContextPtr ctxt G_GNUC_UNUSED,
217 218 219 220 221 222 223
                                                const char *service,
                                                const char *localAddr,
                                                const char *remoteAddr)
{
    virNetSASLSessionPtr sasl = NULL;
    int err;

224
    if (!(sasl = virObjectLockableNew(virNetSASLSessionClass)))
225 226
        return NULL;

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

    return sasl;

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

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

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

    ret = 0;

269
 cleanup:
270
    virObjectUnlock(sasl);
271
    return ret;
272 273 274 275
}

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

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

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


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

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

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

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

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

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

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

352 353
    ret = 0;

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


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

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

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

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

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

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

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

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

    *clientoutlen = outlen;

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

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


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;
475 476
    int err;
    int ret = -1;
477

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

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

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

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

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;
523 524 525
    int err;
    int ret = -1;

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

    *serveroutlen = outlen;

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

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


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

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

    *serveroutlen = outlen;

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

600
 cleanup:
601
    virObjectUnlock(sasl);
602
    return ret;
603 604 605 606
}

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

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;
623
    ssize_t ret = -1;
624

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

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

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

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

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;
662
    ssize_t ret = -1;
663

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

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

686
 cleanup:
687
    virObjectUnlock(sasl);
688
    return ret;
689 690
}

J
Ján Tomko 已提交
691
void virNetSASLContextDispose(void *obj G_GNUC_UNUSED)
692 693 694 695
{
    return;
}

696
void virNetSASLSessionDispose(void *obj)
697
{
698
    virNetSASLSessionPtr sasl = obj;
699 700 701

    if (sasl->conn)
        sasl_dispose(&sasl->conn);
702
    VIR_FREE(sasl->callbacks);
703
}
704 705 706 707

#ifdef __APPLE__
VIR_WARNINGS_RESET
#endif