~ubuntu-branches/ubuntu/precise/sitecopy/precise

« back to all changes in this revision

Viewing changes to lib/neon/ne_auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Sandro Tosi
  • Date: 2008-07-22 07:31:05 UTC
  • mfrom: (1.1.4 upstream) (4.1.7 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080722073105-cbqs1hnc2wvqejfd
Tags: 1:0.16.6-1
* New upstream release
  - fix a crash with progress bar enabled; Closes: #486378
* debian/control
  - set myself as maintainer, Kartik as uploader
  - set Vcs-{Browser,Git} fields
  - bump Standards-Version to 3.8.0
    + debian/README.source added
  - added DM-Upload-Allowed flag
* debian/patches/05_libneon27_transition.dpatch
  - removed since merged upstream
* debian/copyrightdebian/copyright
  - updated upstream email and copyright years
* debian/patches/10_bts410703_preserve_storage_files_sigint.dpatch
  - added to preserve storage files if SIGINT (Ctrl+C) is sent to sitecopy;
    thanks to Andreas Henriksson for the patch; Closes: #410703

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* 
2
2
   HTTP Authentication routines
3
 
   Copyright (C) 1999-2006, Joe Orton <joe@manyfish.co.uk>
 
3
   Copyright (C) 1999-2008, Joe Orton <joe@manyfish.co.uk>
4
4
 
5
5
   This library is free software; you can redistribute it and/or
6
6
   modify it under the terms of the GNU Library General Public
45
45
 
46
46
#ifdef HAVE_OPENSSL
47
47
#include <openssl/rand.h>
 
48
#elif defined(HAVE_GNUTLS)
 
49
#include <gcrypt.h>
48
50
#endif
49
51
 
 
52
#include <errno.h>
50
53
#include <time.h>
51
54
 
52
55
#include "ne_md5.h"
74
77
#include "ne_sspi.h"
75
78
#endif
76
79
 
77
 
#if defined(HAVE_GSSAPI) || defined(HAVE_SSPI)
78
 
#include "ne_private.h" /* only need to extract proxy hostname */
79
 
#endif
80
 
 
81
80
#define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth"
82
81
#define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth"
83
82
 
100
99
 
101
100
    ne_auth_creds creds;
102
101
    void *userdata;
 
102
    int attempt; /* number of invocations of this callback for
 
103
                  * current request. */
103
104
    
104
105
    struct auth_handler *next;
105
106
};
108
109
struct auth_challenge {
109
110
    const struct auth_protocol *protocol;
110
111
    struct auth_handler *handler;
111
 
    const char *realm;
112
 
    const char *nonce;
113
 
    const char *opaque;
 
112
    const char *realm, *nonce, *opaque, *domain;
114
113
    unsigned int stale; /* if stale=true */
115
114
    unsigned int got_qop; /* we were given a qop directive */
116
115
    unsigned int qop_auth; /* "auth" token in qop attrib */
120
119
 
121
120
static const struct auth_class {
122
121
    const char *id, *req_hdr, *resp_hdr, *resp_info_hdr;
123
 
    int status_code, fail_code;
 
122
    int status_code; /* Response status-code to trap. */
 
123
    int fail_code;   /* NE_* request to fail with. */
 
124
    const char *error_noauth; /* Error message template use when
 
125
                               * giving up authentication attempts. */
124
126
} ah_server_class = {
125
127
    HOOK_SERVER_ID,
126
128
    "Authorization", "WWW-Authenticate", "Authentication-Info",
127
 
    401, NE_AUTH
 
129
    401, NE_AUTH,
 
130
    N_("Could not authenticate to server: %s")
128
131
}, ah_proxy_class = {
129
132
    HOOK_PROXY_ID,
130
133
    "Proxy-Authorization", "Proxy-Authenticate", "Proxy-Authentication-Info",
131
 
    407, NE_PROXYAUTH 
 
134
    407, NE_PROXYAUTH,
 
135
    N_("Could not authenticate to proxy server: %s")
132
136
};
133
137
 
134
138
/* Authentication session state. */
142
146
        AUTH_NOTCONNECT /* only in non-CONNECT responsees */
143
147
    } context;
144
148
    
145
 
    /* Specifics for server/proxy auth. FIXME: need a better field
146
 
     * name! */
 
149
    /* Protocol type for server/proxy auth. */
147
150
    const struct auth_class *spec;
148
151
    
149
152
    /* The protocol used for this authentication session */
175
178
    char *nonce;
176
179
    char *cnonce;
177
180
    char *opaque;
 
181
    char **domains; /* list of paths given as domain. */
 
182
    size_t ndomains; /* size of domains array */
178
183
    auth_qop qop;
179
184
    auth_algorithm alg;
180
185
    unsigned int nonce_count;
184
189
    /* Temporary store for half of the Request-Digest
185
190
     * (an optimisation - used in the response-digest calculation) */
186
191
    struct ne_md5_ctx *stored_rdig;
187
 
 
188
 
    /* Details of server... needed to reconstruct absoluteURI's when
189
 
     * necessary */
190
 
    const char *host;
191
 
    const char *uri_scheme;
192
 
    unsigned int port;
193
192
} auth_session;
194
193
 
195
194
struct auth_request {
210
209
/* Used if this Authentication-Info may be sent for non-40[17]
211
210
 * response for this protocol. */
212
211
#define AUTH_FLAG_VERIFY_NON40x (0x0002)
 
212
/* Used for broken the connection-based auth schemes. */
 
213
#define AUTH_FLAG_CONN_AUTH (0x0004)
213
214
 
214
215
struct auth_protocol {
215
216
    unsigned id; /* public NE_AUTH_* id. */
220
221
    
221
222
    /* Parse the authentication challenge; returns zero on success, or
222
223
     * non-zero if this challenge be handled.  'attempt' is the number
223
 
     * of times the request has been resent due to auth challenges. */
 
224
     * of times the request has been resent due to auth challenges.
 
225
     * On failure, challenge_error() should be used to append an error
 
226
     * message to the error buffer 'errmsg'. */
224
227
    int (*challenge)(auth_session *sess, int attempt,
225
 
                     struct auth_challenge *chall);
 
228
                     struct auth_challenge *chall, ne_buffer **errmsg);
226
229
 
227
230
    /* Return the string to send in the -Authenticate request header:
228
231
     * (ne_malloc-allocated, NUL-terminated string) */
229
232
    char *(*response)(auth_session *sess, struct auth_request *req);
230
233
    
231
 
    /* Parse a Authentication-Info response; returns NE_* error
232
 
     * code. */
 
234
    /* Parse a Authentication-Info response; returns NE_* error code
 
235
     * on failure; on failure, the session error string must be
 
236
     * set. */
233
237
    int (*verify)(struct auth_request *req, auth_session *sess,
234
238
                  const char *value);
235
239
    
236
240
    int flags; /* AUTH_FLAG_* flags */
237
241
};
238
242
 
239
 
/* Forward-declaration of protocols array. */
240
 
static const struct auth_protocol protocols[];
 
243
/* Helper function to append an error to the buffer during challenge
 
244
 * handling.  Pass printf-style string.  *errmsg may be NULL and is
 
245
 * allocated if necessary.  errmsg must be non-NULL. */
 
246
static void challenge_error(ne_buffer **errmsg, const char *fmt, ...)
 
247
    ne_attribute((format(printf, 2, 3)));
 
248
 
 
249
/* Free the domains array, precondition sess->ndomains > 0. */
 
250
static void free_domains(auth_session *sess)
 
251
{
 
252
    do {
 
253
        ne_free(sess->domains[sess->ndomains - 1]);
 
254
    } while (--sess->ndomains);
 
255
    ne_free(sess->domains);
 
256
    sess->domains = NULL;
 
257
}
241
258
 
242
259
static void clean_session(auth_session *sess) 
243
260
{
252
269
        ne_md5_destroy_ctx(sess->stored_rdig);
253
270
        sess->stored_rdig = NULL;
254
271
    }
 
272
    if (sess->ndomains) free_domains(sess);
255
273
#ifdef HAVE_GSSAPI
256
274
    {
257
275
        unsigned int major;
280
298
 
281
299
    hash = ne_md5_create_ctx();
282
300
 
283
 
#ifdef HAVE_OPENSSL
284
 
    if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0)
 
301
#ifdef HAVE_GNUTLS
 
302
    if (1) {
 
303
        gcry_create_nonce(data, sizeof data);
 
304
        ne_md5_process_bytes(data, sizeof data, hash);
 
305
    }
 
306
    else
 
307
#elif defined(HAVE_OPENSSL)
 
308
    if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0) {
285
309
        ne_md5_process_bytes(data, sizeof data, hash);
286
 
    else {
287
 
#endif
288
 
    /* Fallback sources of random data: all bad, but no good sources
289
 
     * are available. */
290
 
 
291
 
    /* Uninitialized stack data; yes, happy valgrinders, this is
292
 
     * supposed to be here. */
293
 
    ne_md5_process_bytes(data, sizeof data, hash);
294
 
    
 
310
    } 
 
311
    else 
 
312
#endif /* HAVE_OPENSSL */
 
313
    {
 
314
        /* Fallback sources of random data: all bad, but no good sources
 
315
         * are available. */
 
316
        
 
317
        /* Uninitialized stack data; yes, happy valgrinders, this is
 
318
         * supposed to be here. */
 
319
        ne_md5_process_bytes(data, sizeof data, hash);
 
320
        
 
321
        {
295
322
#ifdef HAVE_GETTIMEOFDAY
296
 
    {
297
 
        struct timeval tv;
298
 
        if (gettimeofday(&tv, NULL) == 0)
299
 
            ne_md5_process_bytes(&tv, sizeof tv, hash);
300
 
    }
 
323
            struct timeval tv;
 
324
            if (gettimeofday(&tv, NULL) == 0)
 
325
                ne_md5_process_bytes(&tv, sizeof tv, hash);
301
326
#else /* HAVE_GETTIMEOFDAY */
302
 
    {
303
 
        time_t t = time(NULL);
304
 
        ne_md5_process_bytes(&t, sizeof t, hash);
305
 
    }
 
327
            time_t t = time(NULL);
 
328
            ne_md5_process_bytes(&t, sizeof t, hash);
306
329
#endif
307
 
    {
 
330
        }
 
331
        {
308
332
#ifdef WIN32
309
 
        DWORD pid = GetCurrentThreadId();
 
333
            DWORD pid = GetCurrentThreadId();
310
334
#else
311
 
        pid_t pid = getpid();
312
 
#endif
313
 
        ne_md5_process_bytes(&pid, sizeof pid, hash);
314
 
    }
315
 
 
316
 
#ifdef HAVE_OPENSSL
317
 
    }
318
 
#endif
 
335
            pid_t pid = getpid();
 
336
#endif
 
337
            ne_md5_process_bytes(&pid, sizeof pid, hash);
 
338
        }
 
339
    }
319
340
 
320
341
    ne_md5_finish_ascii(hash, ret);
321
342
    ne_md5_destroy_ctx(hash);
323
344
    return ne_strdup(ret);
324
345
}
325
346
 
326
 
static int get_credentials(auth_session *sess, int attempt,
 
347
/* Callback to retrieve user credentials for given session on given
 
348
 * attempt (pre request) for given challenge.  Password is written to
 
349
 * pwbuf (of size NE_ABUFSIZ.  On error, challenge_error() is used
 
350
 * with errmsg. */
 
351
static int get_credentials(auth_session *sess, ne_buffer **errmsg, int attempt,
327
352
                           struct auth_challenge *chall, char *pwbuf) 
328
353
{
329
 
    return chall->handler->creds(chall->handler->userdata, sess->realm, 
330
 
                                 attempt, sess->username, pwbuf);
 
354
    if (chall->handler->creds(chall->handler->userdata, sess->realm, 
 
355
                              chall->handler->attempt++, sess->username, pwbuf) == 0) {
 
356
        return 0;
 
357
    } else {
 
358
        challenge_error(errmsg, _("rejected %s challenge"), 
 
359
                        chall->protocol->name);
 
360
        return -1;
 
361
    }
331
362
}
332
363
 
333
364
/* Examine a Basic auth challenge.
334
365
 * Returns 0 if an valid challenge, else non-zero. */
335
366
static int basic_challenge(auth_session *sess, int attempt,
336
 
                           struct auth_challenge *parms) 
 
367
                           struct auth_challenge *parms,
 
368
                           ne_buffer **errmsg) 
337
369
{
338
370
    char *tmp, password[NE_ABUFSIZ];
339
371
 
340
372
    /* Verify challenge... must have a realm */
341
373
    if (parms->realm == NULL) {
 
374
        challenge_error(errmsg, _("missing realm in Basic challenge"));
342
375
        return -1;
343
376
    }
344
377
 
346
379
    
347
380
    sess->realm = ne_strdup(parms->realm);
348
381
 
349
 
    if (get_credentials(sess, attempt, parms, password)) {
 
382
    if (get_credentials(sess, errmsg, attempt, parms, password)) {
350
383
        /* Failed to get credentials */
351
384
        return -1;
352
385
    }
419
452
}
420
453
 
421
454
/* Continue a GSS-API Negotiate exchange, using input TOKEN if
422
 
 * non-NULL.  Returns non-zero on error. */
423
 
static int continue_negotiate(auth_session *sess, const char *token)
 
455
 * non-NULL.  Returns non-zero on error, in which case *errmsg is
 
456
 * guaranteed to be non-NULL (i.e. an error message is set). */
 
457
static int continue_negotiate(auth_session *sess, const char *token,
 
458
                              ne_buffer **errmsg)
424
459
{
425
460
    unsigned int major, minor;
426
461
    gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
431
466
    if (token) {
432
467
        input.length = ne_unbase64(token, &bintoken);
433
468
        if (input.length == 0) {
434
 
            NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n",
435
 
                     token);
 
469
            challenge_error(errmsg, _("invalid Negotiate token"));
436
470
            return -1;
437
471
        }
438
472
        input.value = bintoken;
453
487
    if (bintoken) ne_free(bintoken);
454
488
 
455
489
    if (GSS_ERROR(major)) {
456
 
        ne_buffer *err = ne_buffer_create();
457
490
        int flag = 0;
458
491
 
459
 
        make_gss_error(err, &flag, major, GSS_C_GSS_CODE);
460
 
        make_gss_error(err, &flag, minor, GSS_C_MECH_CODE);
461
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data);
462
 
        ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"), 
463
 
                     err->data);
464
 
        ne_buffer_destroy(err);
 
492
        challenge_error(errmsg, _("GSSAPI authentication error: "));
 
493
        make_gss_error(*errmsg, &flag, major, GSS_C_GSS_CODE);
 
494
        make_gss_error(*errmsg, &flag, minor, GSS_C_MECH_CODE);
 
495
 
465
496
        return -1;
466
497
    }
467
498
 
471
502
        ret = 0;
472
503
    } 
473
504
    else {
474
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major);
 
505
        challenge_error(errmsg, _("GSSAPI failure (code %u)"), major);
475
506
        ret = -1;
476
507
    }
477
508
 
495
526
/* Process a Negotiate challange CHALL in session SESS; returns zero
496
527
 * if challenge is accepted. */
497
528
static int negotiate_challenge(auth_session *sess, int attempt,
498
 
                               struct auth_challenge *chall) 
 
529
                               struct auth_challenge *chall,
 
530
                               ne_buffer **errmsg) 
499
531
{
500
532
    const char *token = chall->opaque;
501
533
 
502
534
    /* Respect an initial challenge - which must have no input token,
503
535
     * or a continuation - which must have an input token. */
504
536
    if (attempt == 0 || token) {
505
 
        return continue_negotiate(sess, token);
 
537
        return continue_negotiate(sess, token, errmsg);
506
538
    }
507
539
    else {
508
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Ignoring empty Negotiate "
509
 
                 "challenge (attempt=%d).\n", attempt);
 
540
        challenge_error(errmsg, _("ignoring empty Negotiate continuation"));
510
541
        return -1;
511
542
    }
512
543
}
518
549
    char *duphdr = ne_strdup(hdr);
519
550
    char *sep, *ptr = strchr(duphdr, ' ');
520
551
    int ret;
 
552
    ne_buffer *errmsg = NULL;
521
553
 
522
 
    if (strncmp(hdr, "Negotiate", ptr - hdr) != 0) {
523
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Not a Negotiate response!\n");
 
554
    if (strncmp(hdr, "Negotiate", ptr - duphdr) != 0) {
 
555
        ne_set_error(sess->sess, _("Negotiate response verification failed: "
 
556
                                   "invalid response header token"));
524
557
        ne_free(duphdr);
525
558
        return NE_ERROR;
526
559
    }
539
572
        *sep = '\0';
540
573
 
541
574
    NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Negotiate response token [%s]\n", ptr);
542
 
    ret = continue_negotiate(sess, ptr);
 
575
    ret = continue_negotiate(sess, ptr, &errmsg);
543
576
    if (ret) {
544
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Mutual auth failed.\n");
 
577
        ne_set_error(sess->sess, _("Negotiate response verification failure: %s"),
 
578
                     errmsg->data);
545
579
    }
 
580
 
 
581
    if (errmsg) ne_buffer_destroy(errmsg);
546
582
    ne_free(duphdr);
 
583
 
547
584
    return ret ? NE_ERROR : NE_OK;
548
585
}
549
586
#endif
550
587
 
551
588
#ifdef HAVE_SSPI
552
 
static char *request_sspi(auth_session *sess) 
 
589
static char *request_sspi(auth_session *sess, struct auth_request *request) 
553
590
{
554
591
    return ne_concat(sess->protocol->name, " ", sess->sspi_token, "\r\n", NULL);
555
592
}
556
593
 
557
594
static int sspi_challenge(auth_session *sess, int attempt,
558
 
                          struct auth_challenge *parms) 
 
595
                          struct auth_challenge *parms,
 
596
                          ne_buffer **errmsg) 
559
597
{
560
598
    int ntlm = ne_strcasecmp(parms->protocol->name, "NTLM") == 0;
561
599
    int status;
564
602
    NE_DEBUG(NE_DBG_HTTPAUTH, "auth: SSPI challenge.\n");
565
603
    
566
604
    if (!sess->sspi_context) {
567
 
        status = ne_sspi_create_context(&sess->sspi_context,
568
 
                                        sess->sess->server.hostname, ntlm);
 
605
        ne_uri uri = {0};
 
606
 
 
607
        ne_fill_server_uri(sess->sess, &uri);
 
608
 
 
609
        status = ne_sspi_create_context(&sess->sspi_context, uri.host, ntlm);
 
610
 
 
611
        ne_uri_free(&uri);
 
612
 
569
613
        if (status) {
570
614
            return status;
571
615
        }
584
628
}
585
629
#endif
586
630
 
 
631
/* Parse the "domain" challenge parameter and set the domains array up
 
632
 * in the session appropriately. */
 
633
static int parse_domain(auth_session *sess, const char *domain)
 
634
{
 
635
    char *cp = ne_strdup(domain), *p = cp;
 
636
    ne_uri base;
 
637
    int invalid = 0;
 
638
 
 
639
    memset(&base, 0, sizeof base);
 
640
    ne_fill_server_uri(sess->sess, &base);
 
641
 
 
642
    do {
 
643
        char *token = ne_token(&p, ' ');
 
644
        ne_uri rel, absolute;
 
645
        
 
646
        if (ne_uri_parse(token, &rel) == 0) {
 
647
            /* Resolve relative to the Request-URI. */
 
648
            ne_uri_resolve(&base, &rel, &absolute);
 
649
 
 
650
            base.path = absolute.path;
 
651
            
 
652
            /* Ignore URIs not on this server. */
 
653
            if (absolute.path && ne_uri_cmp(&absolute, &base) == 0) {
 
654
                sess->domains = ne_realloc(sess->domains, 
 
655
                                           ++sess->ndomains *
 
656
                                           sizeof(*sess->domains));
 
657
                sess->domains[sess->ndomains - 1] = absolute.path;
 
658
                NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Using domain %s from %s\n",
 
659
                         absolute.path, token);
 
660
                absolute.path = NULL;
 
661
            }
 
662
            else {
 
663
                NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Ignoring domain %s\n",
 
664
                         token);
 
665
            }
 
666
 
 
667
            ne_uri_free(&absolute);
 
668
        }
 
669
        else {
 
670
            invalid = 1;
 
671
        }
 
672
        
 
673
        ne_uri_free(&rel);
 
674
        
 
675
    } while (p && !invalid);
 
676
 
 
677
    if (invalid && sess->ndomains) {
 
678
        free_domains(sess);
 
679
    }
 
680
 
 
681
    ne_free(cp);
 
682
    base.path = NULL;
 
683
    ne_uri_free(&base);
 
684
 
 
685
    return invalid;
 
686
}
 
687
 
587
688
/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
588
689
 * else non-zero. */
589
690
static int digest_challenge(auth_session *sess, int attempt,
590
 
                            struct auth_challenge *parms) 
 
691
                            struct auth_challenge *parms,
 
692
                            ne_buffer **errmsg) 
591
693
{
592
694
    char password[NE_ABUFSIZ];
593
695
 
594
696
    if (parms->alg == auth_alg_unknown) {
595
 
        ne_set_error(sess->sess, _("Unknown algorithm in Digest "
596
 
                                   "authentication challenge"));
 
697
        challenge_error(errmsg, _("unknown algorithm in Digest challenge"));
597
698
        return -1;
598
699
    }
599
700
    else if (parms->alg == auth_alg_md5_sess && !parms->qop_auth) {
600
 
        ne_set_error(sess->sess, _("Incompatible algorithm in Digest "
601
 
                                   "authentication challenge"));
 
701
        challenge_error(errmsg, _("incompatible algorithm in Digest challenge"));
602
702
        return -1;
603
703
    }
604
704
    else if (parms->realm == NULL || parms->nonce == NULL) {
605
 
        NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Digest challenge missing parms.\n");
606
 
        ne_set_error(sess->sess, _("Missing nonce or realm in Digest "
607
 
                                   "authentication challenge"));
 
705
        challenge_error(errmsg, _("missing parameter in Digest challenge"));
608
706
        return -1;
609
707
    }
 
708
    else if (parms->stale && sess->nonce_count == 0) {
 
709
        challenge_error(errmsg, _("initial Digest challenge was stale"));
 
710
        return -1;
 
711
    }
 
712
    else if (parms->stale && (sess->alg != parms->alg
 
713
                              || strcmp(sess->realm, parms->realm))) {
 
714
        /* With stale=true the realm and algorithm cannot change since these
 
715
         * require re-hashing H(A1) which defeats the point. */
 
716
        challenge_error(errmsg, _("stale Digest challenge with new algorithm or realm"));
 
717
        return -1;
 
718
    }
610
719
 
611
720
    if (!parms->stale) {
612
 
        /* Forget the old session details */
613
 
        clean_session(sess);
614
 
 
615
 
        sess->realm = ne_strdup(parms->realm);
616
 
 
617
 
        /* Not a stale response: really need user authentication */
618
 
        if (get_credentials(sess, attempt, parms, password)) {
619
 
            /* Failed to get credentials */
620
 
            return -1;
621
 
        }
622
 
    }
623
 
    sess->alg = parms->alg;
 
721
        /* Non-stale challenge: clear session and request credentials. */
 
722
        clean_session(sess);
 
723
 
 
724
        /* The domain paramater must be parsed after the session is
 
725
         * cleaned; ignore domain for proxy auth. */
 
726
        if (parms->domain && sess->spec == &ah_server_class
 
727
            && parse_domain(sess, parms->domain)) {
 
728
            challenge_error(errmsg, _("could not parse domain in Digest challenge"));
 
729
            return -1;
 
730
        }
 
731
 
 
732
        sess->realm = ne_strdup(parms->realm);
 
733
        sess->alg = parms->alg;
 
734
        sess->cnonce = get_cnonce();
 
735
 
 
736
        if (get_credentials(sess, errmsg, attempt, parms, password)) {
 
737
            /* Failed to get credentials */
 
738
            return -1;
 
739
        }
 
740
    }
 
741
    else {
 
742
        /* Stale challenge: accept a new nonce or opaque. */
 
743
        if (sess->nonce) ne_free(sess->nonce);
 
744
        if (sess->opaque && parms->opaque) ne_free(sess->opaque);
 
745
    }
 
746
    
624
747
    sess->nonce = ne_strdup(parms->nonce);
625
 
    sess->cnonce = get_cnonce();
626
 
    /* TODO: add domain handling. */
627
 
    if (parms->opaque != NULL) {
628
 
        sess->opaque = ne_strdup(parms->opaque); /* don't strip the quotes */
 
748
    if (parms->opaque) {
 
749
        sess->opaque = ne_strdup(parms->opaque);
629
750
    }
630
751
    
631
752
    if (parms->got_qop) {
681
802
    return 0;
682
803
}
683
804
 
 
805
/* Returns non-zero if given Request-URI is inside the authentication
 
806
 * domain defined for the session. */
 
807
static int inside_domain(auth_session *sess, const char *req_uri)
 
808
{
 
809
    int inside = 0;
 
810
    size_t n;
 
811
    ne_uri uri;
 
812
    
 
813
    /* Parse the Request-URI; it will be an absoluteURI if using a
 
814
     * proxy, and possibly '*'. */
 
815
    if (strcmp(req_uri, "*") == 0 || ne_uri_parse(req_uri, &uri) != 0) {
 
816
        /* Presume outside the authentication domain. */
 
817
        return 0;
 
818
    }
 
819
 
 
820
    for (n = 0; n < sess->ndomains && !inside; n++) {
 
821
        const char *d = sess->domains[n];
 
822
        
 
823
        inside = strncmp(uri.path, d, strlen(d)) == 0;
 
824
    }
 
825
    
 
826
    NE_DEBUG(NE_DBG_HTTPAUTH, "auth: '%s' is inside auth domain: %d.\n", 
 
827
             uri.path, inside);
 
828
    ne_uri_free(&uri);
 
829
    
 
830
    return inside;
 
831
}            
 
832
 
684
833
/* Return Digest authentication credentials header value for the given
685
834
 * session. */
686
835
static char *request_digest(auth_session *sess, struct auth_request *req) 
691
840
    const char *qop_value = "auth"; /* qop-value */
692
841
    ne_buffer *ret;
693
842
 
 
843
    /* Do not submit credentials if an auth domain is defined and this
 
844
     * request-uri fails outside it. */
 
845
    if (sess->ndomains && !inside_domain(sess, req->uri)) {
 
846
        return NULL;
 
847
    }
 
848
 
694
849
    /* Increase the nonce-count */
695
850
    if (sess->qop != auth_qop_none) {
696
851
        sess->nonce_count++;
868
1023
            cnonce = val;
869
1024
        } else if (ne_strcasecmp(key, "nc") == 0) { 
870
1025
            nc = val;
871
 
            if (sscanf(val, "%x", &nonce_count) != 1) {
872
 
                NE_DEBUG(NE_DBG_HTTPAUTH, "Couldn't find nonce count.\n");
873
 
            } else {
874
 
                NE_DEBUG(NE_DBG_HTTPAUTH, "Got nonce_count: %u\n", nonce_count);
875
 
            }
876
 
        }
 
1026
        }
877
1027
    }
878
1028
 
879
1029
    if (qop == auth_qop_none) {
891
1041
        ne_set_error(sess->sess, _("Digest mutual authentication failure: "
892
1042
                                   "client nonce mismatch"));
893
1043
    }
894
 
    else if (nonce_count != sess->nonce_count) {
895
 
        ret = NE_ERROR;
896
 
        ne_set_error(sess->sess, _("Digest mutual authentication failure: "
897
 
                                   "nonce count mismatch (%u not %u)"),
898
 
                     nonce_count, sess->nonce_count);
 
1044
    else if (nc) {
 
1045
        char *ptr;
 
1046
        
 
1047
        errno = 0;
 
1048
        nonce_count = strtoul(nc, &ptr, 16);
 
1049
        if (*ptr != '\0' || errno) {
 
1050
            ret = NE_ERROR;
 
1051
            ne_set_error(sess->sess, _("Digest mutual authentication failure: "
 
1052
                                       "could not parse nonce count"));
 
1053
        }
 
1054
        else if (nonce_count != sess->nonce_count) {
 
1055
            ret = NE_ERROR;
 
1056
            ne_set_error(sess->sess, _("Digest mutual authentication failure: "
 
1057
                                       "nonce count mismatch (%u not %u)"),
 
1058
                         nonce_count, sess->nonce_count);
 
1059
        }
899
1060
    }
900
 
    else {
901
 
        /* Verify the response-digest field */
 
1061
 
 
1062
    /* Finally, for qop=auth cases, if everything else is OK, verify
 
1063
     * the response-digest field. */    
 
1064
    if (qop == auth_qop_auth && ret == NE_OK) {
902
1065
        struct ne_md5_ctx *a2;
903
1066
        char a2_md5_ascii[33], rdig_md5_ascii[33];
904
1067
 
941
1104
    /* Check for a nextnonce */
942
1105
    if (nextnonce != NULL) {
943
1106
        NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Found nextnonce of [%s].\n", nextnonce);
944
 
        if (sess->nonce != NULL)
945
 
            ne_free(sess->nonce);
 
1107
        ne_free(sess->nonce);
946
1108
        sess->nonce = ne_strdup(nextnonce);
 
1109
        sess->nonce_count = 0;
947
1110
    }
948
1111
 
949
1112
    ne_free(hdr);
951
1114
    return ret;
952
1115
}
953
1116
 
 
1117
static const struct auth_protocol protocols[] = {
 
1118
    { NE_AUTH_BASIC, 10, "Basic",
 
1119
      basic_challenge, request_basic, NULL,
 
1120
      0 },
 
1121
    { NE_AUTH_DIGEST, 20, "Digest",
 
1122
      digest_challenge, request_digest, verify_digest_response,
 
1123
      0 },
 
1124
#ifdef HAVE_GSSAPI
 
1125
    { NE_AUTH_NEGOTIATE, 30, "Negotiate",
 
1126
      negotiate_challenge, request_negotiate, verify_negotiate_response,
 
1127
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x|AUTH_FLAG_CONN_AUTH },
 
1128
#endif
 
1129
#ifdef HAVE_SSPI
 
1130
    { NE_AUTH_NEGOTIATE, 30, "NTLM",
 
1131
      sspi_challenge, request_sspi, NULL,
 
1132
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x|AUTH_FLAG_CONN_AUTH },
 
1133
    { NE_AUTH_NEGOTIATE, 30, "Negotiate",
 
1134
      sspi_challenge, request_sspi, NULL,
 
1135
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x|AUTH_FLAG_CONN_AUTH },
 
1136
#endif
 
1137
    { 0 }
 
1138
};
 
1139
 
954
1140
/* Insert a new auth challenge for protocol 'proto' in list of
955
1141
 * challenges 'list'.  The challenge list is kept in sorted order of
956
1142
 * strength, with highest strength first. */
980
1166
    return ret;
981
1167
}
982
1168
 
 
1169
static void challenge_error(ne_buffer **errbuf, const char *fmt, ...)
 
1170
{
 
1171
    char err[128];
 
1172
    va_list ap;
 
1173
    size_t len;
 
1174
    
 
1175
    va_start(ap, fmt);
 
1176
    len = ne_vsnprintf(err, sizeof err, fmt, ap);
 
1177
    va_end(ap);
 
1178
    
 
1179
    if (*errbuf == NULL) {
 
1180
        *errbuf = ne_buffer_create();
 
1181
        ne_buffer_append(*errbuf, err, len);
 
1182
    }
 
1183
    else {
 
1184
        ne_buffer_concat(*errbuf, ", ", err, NULL);
 
1185
    }
 
1186
}
 
1187
 
983
1188
/* Passed the value of a "(Proxy,WWW)-Authenticate: " header field.
984
1189
 * Returns 0 if valid challenge was accepted; non-zero if no valid
985
1190
 * challenge was found. */
988
1193
{
989
1194
    char *pnt, *key, *val, *hdr, sep;
990
1195
    struct auth_challenge *chall = NULL, *challenges = NULL;
 
1196
    ne_buffer *errmsg = NULL;
991
1197
 
992
1198
    pnt = hdr = ne_strdup(value); 
993
1199
 
1014
1220
            }
1015
1221
 
1016
1222
            if (proto == NULL) {
1017
 
                NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Ignoring '%s' challenge.\n", key);
 
1223
                /* Ignore this challenge. */
1018
1224
                chall = NULL;
 
1225
                challenge_error(&errmsg, _("ignored %s challenge"), key);
1019
1226
                continue;
1020
1227
            }
1021
1228
            
1071
1278
            
1072
1279
            chall->got_qop = chall->qop_auth;
1073
1280
        }
 
1281
        else if (ne_strcasecmp(key, "domain") == 0) {
 
1282
            chall->domain = val;
 
1283
        }
1074
1284
    }
1075
1285
    
1076
1286
    sess->protocol = NULL;
1080
1290
    for (chall = challenges; chall != NULL; chall = chall->next) {
1081
1291
        NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Trying %s challenge...\n",
1082
1292
                 chall->protocol->name);
1083
 
        if (chall->protocol->challenge(sess, attempt, chall) == 0) {
 
1293
        if (chall->protocol->challenge(sess, attempt, chall, &errmsg) == 0) {
 
1294
            NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Accepted %s challenge.\n", 
 
1295
                     chall->protocol->name);
1084
1296
            sess->protocol = chall->protocol;
1085
1297
            break;
1086
1298
        }
1088
1300
 
1089
1301
    if (!sess->protocol) {
1090
1302
        NE_DEBUG(NE_DBG_HTTPAUTH, "auth: No challenges accepted.\n");
 
1303
        ne_set_error(sess->sess, _(sess->spec->error_noauth),
 
1304
                     errmsg ? errmsg->data : _("could not parse challenge"));
1091
1305
    }
1092
1306
 
1093
1307
    while (challenges != NULL) {
1097
1311
    }
1098
1312
 
1099
1313
    ne_free(hdr);
 
1314
    if (errmsg) ne_buffer_destroy(errmsg);
1100
1315
 
1101
1316
    return !(sess->protocol != NULL);
1102
1317
}
1111
1326
        (is_connect && sess->context == AUTH_CONNECT) ||
1112
1327
        (!is_connect && sess->context == AUTH_NOTCONNECT)) {
1113
1328
        struct auth_request *areq = ne_calloc(sizeof *areq);
 
1329
        struct auth_handler *hdl;
1114
1330
        
1115
1331
        NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->spec->resp_hdr);
1116
1332
        
1119
1335
        areq->request = req;
1120
1336
        
1121
1337
        ne_set_request_private(req, sess->spec->id, areq);
 
1338
 
 
1339
        /* For each new request, reset the attempt counter in every
 
1340
         * registered handler. */
 
1341
        for (hdl = sess->handlers; hdl; hdl = hdl->next) {
 
1342
            hdl->attempt = 0;
 
1343
        }
1122
1344
    }
1123
1345
}
1124
1346
 
1140
1362
            ne_buffer_concat(request, sess->spec->req_hdr, ": ", value, NULL);
1141
1363
            ne_free(value);
1142
1364
        }
1143
 
 
1144
1365
    }
1145
1366
 
1146
1367
}
1180
1401
        && (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) == 0) {
1181
1402
        ret = sess->protocol->verify(areq, sess, auth_info_hdr);
1182
1403
    }
1183
 
    else if (sess->protocol
1184
 
             && sess->protocol->flags && AUTH_FLAG_VERIFY_NON40x
 
1404
    else if (sess->protocol && sess->protocol->verify
 
1405
             && (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) 
1185
1406
             && (status->klass == 2 || status->klass == 3)
1186
1407
             && auth_hdr) {
1187
1408
        ret = sess->protocol->verify(areq, sess, auth_hdr);
1198
1419
            clean_session(sess);
1199
1420
            ret = sess->spec->fail_code;
1200
1421
        }
 
1422
        
 
1423
        /* Set or clear the conn-auth flag according to whether this
 
1424
         * was an accepted challenge for a borked protocol. */
 
1425
        ne_set_session_flag(sess->sess, NE_SESSFLAG_CONNAUTH,
 
1426
                            sess->protocol 
 
1427
                            && (sess->protocol->flags & AUTH_FLAG_CONN_AUTH));
1201
1428
    }
1202
1429
#ifdef HAVE_SSPI
1203
1430
    else if (sess->sspi_context) {
1246
1473
    auth_session *ahs;
1247
1474
    struct auth_handler **hdl;
1248
1475
 
 
1476
    /* Handle the _ALL and _DEFAULT protocol masks: */
 
1477
    if (protomask == NE_AUTH_ALL) {
 
1478
        protomask |= NE_AUTH_BASIC | NE_AUTH_DIGEST | NE_AUTH_NEGOTIATE;
 
1479
    }
 
1480
    else if (protomask == NE_AUTH_DEFAULT) {
 
1481
        protomask |= NE_AUTH_BASIC | NE_AUTH_DIGEST;
 
1482
        
 
1483
        if (strcmp(ne_get_scheme(sess), "https") == 0 || isproxy) {
 
1484
            protomask |= NE_AUTH_NEGOTIATE;
 
1485
        }
 
1486
    }
 
1487
 
1249
1488
    ahs = ne_get_session_private(sess, id);
1250
1489
    if (ahs == NULL) {
1251
1490
        ahs = ne_calloc(sizeof *ahs);
1271
1510
 
1272
1511
#ifdef HAVE_GSSAPI
1273
1512
    if (protomask & NE_AUTH_NEGOTIATE && ahs->gssname == GSS_C_NO_NAME) {
1274
 
        get_gss_name(&ahs->gssname, (isproxy ? sess->proxy.hostname 
1275
 
                                     : sess->server.hostname));
 
1513
        ne_uri uri = {0};
 
1514
        
 
1515
        if (isproxy)
 
1516
            ne_fill_proxy_uri(sess, &uri);
 
1517
        else
 
1518
            ne_fill_server_uri(sess, &uri);
 
1519
 
 
1520
        get_gss_name(&ahs->gssname, uri.host);
 
1521
 
 
1522
        ne_uri_free(&uri);
1276
1523
    }
1277
1524
#endif
1278
1525
 
1279
1526
    /* Find the end of the handler list, and add a new one. */
1280
1527
    hdl = &ahs->handlers;
1281
 
    while (*hdl && (*hdl)->next)
 
1528
    while (*hdl)
1282
1529
        hdl = &(*hdl)->next;
1283
1530
        
1284
1531
    *hdl = ne_malloc(sizeof **hdl);
1286
1533
    (*hdl)->creds = creds;
1287
1534
    (*hdl)->userdata = userdata;
1288
1535
    (*hdl)->next = NULL;
1289
 
}
1290
 
 
1291
 
static void auth_register_default(ne_session *sess, int isproxy,
1292
 
                                  const struct auth_class *ahc, const char *id,
1293
 
                                  ne_auth_creds creds, void *userdata) 
1294
 
{
1295
 
    unsigned protomask = NE_AUTH_BASIC|NE_AUTH_DIGEST;
1296
 
    
1297
 
    if (strcmp(ne_get_scheme(sess), "https") == 0 || isproxy) {
1298
 
        protomask |= NE_AUTH_NEGOTIATE;
1299
 
    }
1300
 
 
1301
 
    auth_register(sess, isproxy, protomask, ahc, id, creds, userdata);
1302
 
}
1303
 
 
1304
 
static const struct auth_protocol protocols[] = {
1305
 
    { NE_AUTH_BASIC, 10, "Basic",
1306
 
      basic_challenge, request_basic, NULL,
1307
 
      0 },
1308
 
    { NE_AUTH_DIGEST, 20, "Digest",
1309
 
      digest_challenge, request_digest, verify_digest_response,
1310
 
      0 },
1311
 
#ifdef HAVE_GSSAPI
1312
 
    { NE_AUTH_NEGOTIATE, 30, "Negotiate",
1313
 
      negotiate_challenge, request_negotiate, verify_negotiate_response,
1314
 
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x },
1315
 
#endif
1316
 
#ifdef HAVE_SSPI
1317
 
    { NE_AUTH_NEGOTIATE, 30, "NTLM",
1318
 
      sspi_challenge, request_sspi, NULL,
1319
 
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x },
1320
 
    { NE_AUTH_NEGOTIATE, 30, "Negotiate",
1321
 
      sspi_challenge, request_sspi, NULL,
1322
 
      AUTH_FLAG_OPAQUE_PARAM|AUTH_FLAG_VERIFY_NON40x },
1323
 
#endif
1324
 
    { 0 }
1325
 
};
 
1536
    (*hdl)->attempt = 0;
 
1537
}
1326
1538
 
1327
1539
void ne_set_server_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
1328
1540
{
1329
 
    auth_register_default(sess, 0, &ah_server_class, HOOK_SERVER_ID,
1330
 
                          creds, userdata);
 
1541
    auth_register(sess, 0, NE_AUTH_DEFAULT, &ah_server_class, HOOK_SERVER_ID,
 
1542
                  creds, userdata);
1331
1543
}
1332
1544
 
1333
1545
void ne_set_proxy_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
1334
1546
{
1335
 
    auth_register_default(sess, 1, &ah_proxy_class, HOOK_PROXY_ID,
1336
 
                          creds, userdata);
 
1547
    auth_register(sess, 1, NE_AUTH_DEFAULT, &ah_proxy_class, HOOK_PROXY_ID,
 
1548
                  creds, userdata);
1337
1549
}
1338
1550
 
1339
1551
void ne_add_server_auth(ne_session *sess, unsigned protocol,