00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #include "apr_sha1.h"
00058 #include "apr_base64.h"
00059 #include "apr_lib.h"
00060 #include "apr_time.h"
00061 #include "apr_errno.h"
00062 #include "apr_global_mutex.h"
00063 #include "apr_strings.h"
00064
00065 #define APR_WANT_STRFUNC
00066 #include "apr_want.h"
00067
00068 #include "ap_config.h"
00069 #include "httpd.h"
00070 #include "http_config.h"
00071 #include "http_core.h"
00072 #include "http_request.h"
00073 #include "http_log.h"
00074 #include "http_protocol.h"
00075 #include "apr_uri.h"
00076 #include "util_md5.h"
00077 #include "apr_shm.h"
00078 #include "apr_rmm.h"
00079 #include "ap_provider.h"
00080
00081 #include "mod_auth.h"
00082
00083
00084
00085
00086 #undef APR_HAS_SHARED_MEMORY
00087 #define APR_HAS_SHARED_MEMORY 0
00088
00089
00090
00091 typedef struct digest_config_struct {
00092 const char *dir_name;
00093 authn_provider_list *providers;
00094 const char *realm;
00095 char **qop_list;
00096 apr_sha1_ctx_t nonce_ctx;
00097 apr_time_t nonce_lifetime;
00098 const char *nonce_format;
00099 int check_nc;
00100 const char *algorithm;
00101 char *uri_list;
00102 const char *ha1;
00103 } digest_config_rec;
00104
00105
00106 #define DFLT_ALGORITHM "MD5"
00107
00108 #define DFLT_NONCE_LIFE apr_time_from_sec(300)
00109 #define NEXTNONCE_DELTA apr_time_from_sec(30)
00110
00111
00112 #define NONCE_TIME_LEN (((sizeof(apr_time_t)+2)/3)*4)
00113 #define NONCE_HASH_LEN (2*APR_SHA1_DIGESTSIZE)
00114 #define NONCE_LEN (int )(NONCE_TIME_LEN + NONCE_HASH_LEN)
00115
00116 #define SECRET_LEN 20
00117
00118
00119
00120
00121 typedef struct hash_entry {
00122 unsigned long key;
00123 struct hash_entry *next;
00124 unsigned long nonce_count;
00125 char ha1[2*APR_MD5_DIGESTSIZE+1];
00126 char last_nonce[NONCE_LEN+1];
00127 } client_entry;
00128
00129 static struct hash_table {
00130 client_entry **table;
00131 unsigned long tbl_len;
00132 unsigned long num_entries;
00133 unsigned long num_created;
00134 unsigned long num_removed;
00135 unsigned long num_renewed;
00136 } *client_list;
00137
00138
00139
00140
00141 enum hdr_sts { NO_HEADER, NOT_DIGEST, INVALID, VALID };
00142
00143 typedef struct digest_header_struct {
00144 const char *scheme;
00145 const char *realm;
00146 const char *username;
00147 char *nonce;
00148 const char *uri;
00149 const char *method;
00150 const char *digest;
00151 const char *algorithm;
00152 const char *cnonce;
00153 const char *opaque;
00154 unsigned long opaque_num;
00155 const char *message_qop;
00156 const char *nonce_count;
00157
00158 apr_time_t nonce_time;
00159 enum hdr_sts auth_hdr_sts;
00160 const char *raw_request_uri;
00161 apr_uri_t *psd_request_uri;
00162 int needed_auth;
00163 client_entry *client;
00164 } digest_header_rec;
00165
00166
00167
00168
00169 typedef union time_union {
00170 apr_time_t time;
00171 unsigned char arr[sizeof(apr_time_t)];
00172 } time_rec;
00173
00174 static unsigned char secret[SECRET_LEN];
00175
00176
00177
00178 static apr_shm_t *client_shm = NULL;
00179 static apr_rmm_t *client_rmm = NULL;
00180 static unsigned long *opaque_cntr;
00181 static apr_time_t *otn_counter;
00182 static apr_global_mutex_t *client_lock = NULL;
00183 static apr_global_mutex_t *opaque_lock = NULL;
00184 static char client_lock_name[L_tmpnam];
00185 static char opaque_lock_name[L_tmpnam];
00186
00187 #define DEF_SHMEM_SIZE 1000L
00188 #define DEF_NUM_BUCKETS 15L
00189 #define HASH_DEPTH 5
00190
00191 static long shmem_size = DEF_SHMEM_SIZE;
00192 static long num_buckets = DEF_NUM_BUCKETS;
00193
00194
00195 module AP_MODULE_DECLARE_DATA auth_digest_module;
00196
00197
00198
00199
00200
00201 static apr_status_t cleanup_tables(void *not_used)
00202 {
00203 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
00204 "Digest: cleaning up shared memory");
00205 fflush(stderr);
00206
00207 if (client_shm) {
00208 apr_shm_destroy(client_shm);
00209 client_shm = NULL;
00210 }
00211
00212 if (client_lock) {
00213 apr_global_mutex_destroy(client_lock);
00214 client_lock = NULL;
00215 }
00216
00217 if (opaque_lock) {
00218 apr_global_mutex_destroy(opaque_lock);
00219 opaque_lock = NULL;
00220 }
00221
00222 return APR_SUCCESS;
00223 }
00224
00225 static apr_status_t initialize_secret(server_rec *s)
00226 {
00227 apr_status_t status;
00228
00229 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
00230 "Digest: generating secret for digest authentication ...");
00231
00232 #if APR_HAS_RANDOM
00233 status = apr_generate_random_bytes(secret, sizeof(secret));
00234 #else
00235 #error APR random number support is missing; you probably need to install the truerand library.
00236 #endif
00237
00238 if (status != APR_SUCCESS) {
00239 char buf[120];
00240 ap_log_error(APLOG_MARK, APLOG_CRIT, status, s,
00241 "Digest: error generating secret: %s",
00242 apr_strerror(status, buf, sizeof(buf)));
00243 return status;
00244 }
00245
00246 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "Digest: done");
00247
00248 return APR_SUCCESS;
00249 }
00250
00251 static void log_error_and_cleanup(char *msg, apr_status_t sts, server_rec *s)
00252 {
00253 ap_log_error(APLOG_MARK, APLOG_ERR, sts, s,
00254 "Digest: %s - all nonce-count checking, one-time nonces, and "
00255 "MD5-sess algorithm disabled", msg);
00256
00257 cleanup_tables(NULL);
00258 }
00259
00260 #if APR_HAS_SHARED_MEMORY
00261
00262 static void initialize_tables(server_rec *s, apr_pool_t *ctx)
00263 {
00264 unsigned long idx;
00265 apr_status_t sts;
00266
00267
00268
00269 sts = apr_shm_create(&client_shm, shmem_size, tmpnam(NULL), ctx);
00270 if (sts != APR_SUCCESS) {
00271 log_error_and_cleanup("failed to create shared memory segments", sts, s);
00272 return;
00273 }
00274
00275 client_list = apr_rmm_malloc(client_rmm, sizeof(*client_list) +
00276 sizeof(client_entry*)*num_buckets);
00277 if (!client_list) {
00278 log_error_and_cleanup("failed to allocate shared memory", -1, s);
00279 return;
00280 }
00281 client_list->table = (client_entry**) (client_list + 1);
00282 for (idx = 0; idx < num_buckets; idx++) {
00283 client_list->table[idx] = NULL;
00284 }
00285 client_list->tbl_len = num_buckets;
00286 client_list->num_entries = 0;
00287
00288 tmpnam(client_lock_name);
00289
00290
00291 sts = apr_global_mutex_create(&client_lock, client_lock_name,
00292 APR_LOCK_DEFAULT, ctx);
00293 if (sts != APR_SUCCESS) {
00294 log_error_and_cleanup("failed to create lock (client_lock)", sts, s);
00295 return;
00296 }
00297
00298
00299
00300
00301 opaque_cntr = apr_rmm_malloc(client_rmm, sizeof(*opaque_cntr));
00302 if (opaque_cntr == NULL) {
00303 log_error_and_cleanup("failed to allocate shared memory", -1, s);
00304 return;
00305 }
00306 *opaque_cntr = 1UL;
00307
00308 tmpnam(opaque_lock_name);
00309
00310
00311 sts = apr_global_mutex_create(&opaque_lock, opaque_lock_name,
00312 APR_LOCK_DEFAULT, ctx);
00313 if (sts != APR_SUCCESS) {
00314 log_error_and_cleanup("failed to create lock (opaque_lock)", sts, s);
00315 return;
00316 }
00317
00318
00319
00320
00321 otn_counter = apr_rmm_malloc(client_rmm, sizeof(*otn_counter));
00322 if (otn_counter == NULL) {
00323 log_error_and_cleanup("failed to allocate shared memory", -1, s);
00324 return;
00325 }
00326 *otn_counter = 0;
00327
00328
00329
00330
00331 return;
00332 }
00333
00334 #endif
00335
00336
00337 static int initialize_module(apr_pool_t *p, apr_pool_t *plog,
00338 apr_pool_t *ptemp, server_rec *s)
00339 {
00340 void *data;
00341 const char *userdata_key = "auth_digest_init";
00342
00343
00344
00345
00346 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
00347 if (!data) {
00348 apr_pool_userdata_set((const void *)1, userdata_key,
00349 apr_pool_cleanup_null, s->process->pool);
00350 return OK;
00351 }
00352 if (initialize_secret(s) != APR_SUCCESS) {
00353 return !OK;
00354 }
00355
00356 #if APR_HAS_SHARED_MEMORY
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367 initialize_tables(s, p);
00368 apr_pool_cleanup_register(p, NULL, cleanup_tables, apr_pool_cleanup_null);
00369 #endif
00370 return OK;
00371 }
00372
00373 static void initialize_child(apr_pool_t *p, server_rec *s)
00374 {
00375 apr_status_t sts;
00376
00377 if (!client_shm) {
00378 return;
00379 }
00380
00381
00382
00383 sts = apr_global_mutex_child_init(&client_lock, client_lock_name, p);
00384 if (sts != APR_SUCCESS) {
00385 log_error_and_cleanup("failed to create lock (client_lock)", sts, s);
00386 return;
00387 }
00388
00389
00390 sts = apr_global_mutex_child_init(&opaque_lock, opaque_lock_name, p);
00391 if (sts != APR_SUCCESS) {
00392 log_error_and_cleanup("failed to create lock (opaque_lock)", sts, s);
00393 return;
00394 }
00395 }
00396
00397
00398
00399
00400
00401 static void *create_digest_dir_config(apr_pool_t *p, char *dir)
00402 {
00403 digest_config_rec *conf;
00404
00405 if (dir == NULL) {
00406 return NULL;
00407 }
00408
00409 conf = (digest_config_rec *) apr_pcalloc(p, sizeof(digest_config_rec));
00410 if (conf) {
00411 conf->qop_list = apr_palloc(p, sizeof(char*));
00412 conf->qop_list[0] = NULL;
00413 conf->nonce_lifetime = DFLT_NONCE_LIFE;
00414 conf->dir_name = apr_pstrdup(p, dir);
00415 conf->algorithm = DFLT_ALGORITHM;
00416 }
00417
00418 return conf;
00419 }
00420
00421 static const char *set_realm(cmd_parms *cmd, void *config, const char *realm)
00422 {
00423 digest_config_rec *conf = (digest_config_rec *) config;
00424
00425
00426
00427
00428
00429
00430 conf->realm = realm;
00431
00432
00433
00434
00435
00436 apr_sha1_init(&conf->nonce_ctx);
00437 apr_sha1_update_binary(&conf->nonce_ctx, secret, sizeof(secret));
00438 apr_sha1_update_binary(&conf->nonce_ctx, (const unsigned char *) realm,
00439 strlen(realm));
00440
00441 return DECLINE_CMD;
00442 }
00443
00444 static const char *add_authn_provider(cmd_parms *cmd, void *config,
00445 const char *arg)
00446 {
00447 digest_config_rec *conf = (digest_config_rec*)config;
00448 authn_provider_list *newp;
00449
00450 newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list));
00451 newp->provider_name = apr_pstrdup(cmd->pool, arg);
00452
00453
00454 newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
00455 newp->provider_name, "0");
00456
00457 if (newp->provider == NULL) {
00458
00459
00460 return apr_psprintf(cmd->pool,
00461 "Unknown Authn provider: %s",
00462 newp->provider_name);
00463 }
00464
00465 if (!newp->provider->get_realm_hash) {
00466
00467 return apr_psprintf(cmd->pool,
00468 "The '%s' Authn provider doesn't support "
00469 "Digest Authentication", newp->provider_name);
00470 }
00471
00472
00473 if (!conf->providers) {
00474 conf->providers = newp;
00475 }
00476 else {
00477 authn_provider_list *last = conf->providers;
00478
00479 while (last->next) {
00480 last = last->next;
00481 }
00482 last->next = newp;
00483 }
00484
00485 return NULL;
00486 }
00487
00488 static const char *set_qop(cmd_parms *cmd, void *config, const char *op)
00489 {
00490 digest_config_rec *conf = (digest_config_rec *) config;
00491 char **tmp;
00492 int cnt;
00493
00494 if (!strcasecmp(op, "none")) {
00495 if (conf->qop_list[0] == NULL) {
00496 conf->qop_list = apr_palloc(cmd->pool, 2 * sizeof(char*));
00497 conf->qop_list[1] = NULL;
00498 }
00499 conf->qop_list[0] = "none";
00500 return NULL;
00501 }
00502
00503 if (!strcasecmp(op, "auth-int")) {
00504 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
00505 "Digest: WARNING: qop `auth-int' currently only works "
00506 "correctly for responses with no entity");
00507 }
00508 else if (strcasecmp(op, "auth")) {
00509 return apr_pstrcat(cmd->pool, "Unrecognized qop: ", op, NULL);
00510 }
00511
00512 for (cnt = 0; conf->qop_list[cnt] != NULL; cnt++)
00513 ;
00514
00515 tmp = apr_palloc(cmd->pool, (cnt + 2) * sizeof(char*));
00516 memcpy(tmp, conf->qop_list, cnt*sizeof(char*));
00517 tmp[cnt] = apr_pstrdup(cmd->pool, op);
00518 tmp[cnt+1] = NULL;
00519 conf->qop_list = tmp;
00520
00521 return NULL;
00522 }
00523
00524 static const char *set_nonce_lifetime(cmd_parms *cmd, void *config,
00525 const char *t)
00526 {
00527 char *endptr;
00528 long lifetime;
00529
00530 lifetime = strtol(t, &endptr, 10);
00531 if (endptr < (t+strlen(t)) && !apr_isspace(*endptr)) {
00532 return apr_pstrcat(cmd->pool,
00533 "Invalid time in AuthDigestNonceLifetime: ",
00534 t, NULL);
00535 }
00536
00537 ((digest_config_rec *) config)->nonce_lifetime = apr_time_from_sec(lifetime);
00538 return NULL;
00539 }
00540
00541 static const char *set_nonce_format(cmd_parms *cmd, void *config,
00542 const char *fmt)
00543 {
00544 ((digest_config_rec *) config)->nonce_format = fmt;
00545 return "AuthDigestNonceFormat is not implemented (yet)";
00546 }
00547
00548 static const char *set_nc_check(cmd_parms *cmd, void *config, int flag)
00549 {
00550 if (flag && !client_shm)
00551 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
00552 cmd->server, "Digest: WARNING: nonce-count checking "
00553 "is not supported on platforms without shared-memory "
00554 "support - disabling check");
00555
00556 ((digest_config_rec *) config)->check_nc = flag;
00557 return NULL;
00558 }
00559
00560 static const char *set_algorithm(cmd_parms *cmd, void *config, const char *alg)
00561 {
00562 if (!strcasecmp(alg, "MD5-sess")) {
00563 if (!client_shm) {
00564 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
00565 cmd->server, "Digest: WARNING: algorithm `MD5-sess' "
00566 "is not supported on platforms without shared-memory "
00567 "support - reverting to MD5");
00568 alg = "MD5";
00569 }
00570 }
00571 else if (strcasecmp(alg, "MD5")) {
00572 return apr_pstrcat(cmd->pool, "Invalid algorithm in AuthDigestAlgorithm: ", alg, NULL);
00573 }
00574
00575 ((digest_config_rec *) config)->algorithm = alg;
00576 return NULL;
00577 }
00578
00579 static const char *set_uri_list(cmd_parms *cmd, void *config, const char *uri)
00580 {
00581 digest_config_rec *c = (digest_config_rec *) config;
00582 if (c->uri_list) {
00583 c->uri_list[strlen(c->uri_list)-1] = '\0';
00584 c->uri_list = apr_pstrcat(cmd->pool, c->uri_list, " ", uri, "\"", NULL);
00585 }
00586 else {
00587 c->uri_list = apr_pstrcat(cmd->pool, ", domain=\"", uri, "\"", NULL);
00588 }
00589 return NULL;
00590 }
00591
00592 static const char *set_shmem_size(cmd_parms *cmd, void *config,
00593 const char *size_str)
00594 {
00595 char *endptr;
00596 long size, min;
00597
00598 size = strtol(size_str, &endptr, 10);
00599 while (apr_isspace(*endptr)) endptr++;
00600 if (*endptr == '\0' || *endptr == 'b' || *endptr == 'B') {
00601 ;
00602 }
00603 else if (*endptr == 'k' || *endptr == 'K') {
00604 size *= 1024;
00605 }
00606 else if (*endptr == 'm' || *endptr == 'M') {
00607 size *= 1048576;
00608 }
00609 else {
00610 return apr_pstrcat(cmd->pool, "Invalid size in AuthDigestShmemSize: ",
00611 size_str, NULL);
00612 }
00613
00614 min = sizeof(*client_list) + sizeof(client_entry*) + sizeof(client_entry);
00615 if (size < min) {
00616 return apr_psprintf(cmd->pool, "size in AuthDigestShmemSize too small: "
00617 "%ld < %ld", size, min);
00618 }
00619
00620 shmem_size = size;
00621 num_buckets = (size - sizeof(*client_list)) /
00622 (sizeof(client_entry*) + HASH_DEPTH * sizeof(client_entry));
00623 if (num_buckets == 0) {
00624 num_buckets = 1;
00625 }
00626 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
00627 "Digest: Set shmem-size: %ld, num-buckets: %ld", shmem_size,
00628 num_buckets);
00629
00630 return NULL;
00631 }
00632
00633 static const command_rec digest_cmds[] =
00634 {
00635 AP_INIT_TAKE1("AuthName", set_realm, NULL, OR_AUTHCFG,
00636 "The authentication realm (e.g. \"Members Only\")"),
00637 AP_INIT_ITERATE("AuthDigestProvider", add_authn_provider, NULL, OR_AUTHCFG,
00638 "specify the auth providers for a directory or location"),
00639 AP_INIT_ITERATE("AuthDigestQop", set_qop, NULL, OR_AUTHCFG,
00640 "A list of quality-of-protection options"),
00641 AP_INIT_TAKE1("AuthDigestNonceLifetime", set_nonce_lifetime, NULL, OR_AUTHCFG,
00642 "Maximum lifetime of the server nonce (seconds)"),
00643 AP_INIT_TAKE1("AuthDigestNonceFormat", set_nonce_format, NULL, OR_AUTHCFG,
00644 "The format to use when generating the server nonce"),
00645 AP_INIT_FLAG("AuthDigestNcCheck", set_nc_check, NULL, OR_AUTHCFG,
00646 "Whether or not to check the nonce-count sent by the client"),
00647 AP_INIT_TAKE1("AuthDigestAlgorithm", set_algorithm, NULL, OR_AUTHCFG,
00648 "The algorithm used for the hash calculation"),
00649 AP_INIT_ITERATE("AuthDigestDomain", set_uri_list, NULL, OR_AUTHCFG,
00650 "A list of URI's which belong to the same protection space as the current URI"),
00651 AP_INIT_TAKE1("AuthDigestShmemSize", set_shmem_size, NULL, RSRC_CONF,
00652 "The amount of shared memory to allocate for keeping track of clients"),
00653 {NULL}
00654 };
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 static client_entry *get_client(unsigned long key, const request_rec *r)
00714 {
00715 int bucket;
00716 client_entry *entry, *prev = NULL;
00717
00718
00719 if (!key || !client_shm) return NULL;
00720
00721 bucket = key % client_list->tbl_len;
00722 entry = client_list->table[bucket];
00723
00724 apr_global_mutex_lock(client_lock);
00725
00726 while (entry && key != entry->key) {
00727 prev = entry;
00728 entry = entry->next;
00729 }
00730
00731 if (entry && prev) {
00732 prev->next = entry->next;
00733 entry->next = client_list->table[bucket];
00734 client_list->table[bucket] = entry;
00735 }
00736
00737 apr_global_mutex_unlock(client_lock);
00738
00739 if (entry) {
00740 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
00741 "get_client(): client %lu found", key);
00742 }
00743 else {
00744 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
00745 "get_client(): client %lu not found", key);
00746 }
00747
00748 return entry;
00749 }
00750
00751
00752
00753
00754
00755
00756 static long gc(void)
00757 {
00758 client_entry *entry, *prev;
00759 unsigned long num_removed = 0, idx;
00760
00761
00762
00763 for (idx = 0; idx < client_list->tbl_len; idx++) {
00764 entry = client_list->table[idx];
00765 prev = NULL;
00766 while (entry->next) {
00767 prev = entry;
00768 entry = entry->next;
00769 }
00770 if (prev) {
00771 prev->next = NULL;
00772 }
00773 else {
00774 client_list->table[idx] = NULL;
00775 }
00776 if (entry) {
00777 apr_rmm_free(client_rmm, (apr_rmm_off_t)entry);
00778 num_removed++;
00779 }
00780 }
00781
00782
00783
00784 client_list->num_entries -= num_removed;
00785 client_list->num_removed += num_removed;
00786
00787 return num_removed;
00788 }
00789
00790
00791
00792
00793
00794
00795 static client_entry *add_client(unsigned long key, client_entry *info,
00796 server_rec *s)
00797 {
00798 int bucket;
00799 client_entry *entry;
00800
00801
00802 if (!key || !client_shm) {
00803 return NULL;
00804 }
00805
00806 bucket = key % client_list->tbl_len;
00807 entry = client_list->table[bucket];
00808
00809 apr_global_mutex_lock(client_lock);
00810
00811
00812
00813 entry = (client_entry *)apr_rmm_malloc(client_rmm, sizeof(client_entry));
00814 if (!entry) {
00815 long num_removed = gc();
00816 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
00817 "Digest: gc'd %ld client entries. Total new clients: "
00818 "%ld; Total removed clients: %ld; Total renewed clients: "
00819 "%ld", num_removed,
00820 client_list->num_created - client_list->num_renewed,
00821 client_list->num_removed, client_list->num_renewed);
00822 entry = (client_entry *)apr_rmm_malloc(client_rmm, sizeof(client_entry));
00823 if (!entry) {
00824 return NULL;
00825 }
00826 }
00827
00828
00829
00830 memcpy(entry, info, sizeof(client_entry));
00831 entry->key = key;
00832 entry->next = client_list->table[bucket];
00833 client_list->table[bucket] = entry;
00834 client_list->num_created++;
00835 client_list->num_entries++;
00836
00837 apr_global_mutex_unlock(client_lock);
00838
00839 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
00840 "allocated new client %lu", key);
00841
00842 return entry;
00843 }
00844
00845
00846
00847
00848
00849
00850
00851 static int get_digest_rec(request_rec *r, digest_header_rec *resp)
00852 {
00853 const char *auth_line;
00854 apr_size_t l;
00855 int vk = 0, vv = 0;
00856 char *key, *value;
00857
00858 auth_line = apr_table_get(r->headers_in,
00859 (PROXYREQ_PROXY == r->proxyreq)
00860 ? "Proxy-Authorization"
00861 : "Authorization");
00862 if (!auth_line) {
00863 resp->auth_hdr_sts = NO_HEADER;
00864 return !OK;
00865 }
00866
00867 resp->scheme = ap_getword_white(r->pool, &auth_line);
00868 if (strcasecmp(resp->scheme, "Digest")) {
00869 resp->auth_hdr_sts = NOT_DIGEST;
00870 return !OK;
00871 }
00872
00873 l = strlen(auth_line);
00874
00875 key = apr_palloc(r->pool, l+1);
00876 value = apr_palloc(r->pool, l+1);
00877
00878 while (auth_line[0] != '\0') {
00879
00880
00881
00882 while (apr_isspace(auth_line[0])) {
00883 auth_line++;
00884 }
00885 vk = 0;
00886 while (auth_line[0] != '=' && auth_line[0] != ','
00887 && auth_line[0] != '\0' && !apr_isspace(auth_line[0])) {
00888 key[vk++] = *auth_line++;
00889 }
00890 key[vk] = '\0';
00891 while (apr_isspace(auth_line[0])) {
00892 auth_line++;
00893 }
00894
00895
00896
00897 if (auth_line[0] == '=') {
00898 auth_line++;
00899 while (apr_isspace(auth_line[0])) {
00900 auth_line++;
00901 }
00902
00903 vv = 0;
00904 if (auth_line[0] == '\"') {
00905 auth_line++;
00906 while (auth_line[0] != '\"' && auth_line[0] != '\0') {
00907 if (auth_line[0] == '\\' && auth_line[1] != '\0') {
00908 auth_line++;
00909 }
00910 value[vv++] = *auth_line++;
00911 }
00912 if (auth_line[0] != '\0') {
00913 auth_line++;
00914 }
00915 }
00916 else {
00917 while (auth_line[0] != ',' && auth_line[0] != '\0'
00918 && !apr_isspace(auth_line[0])) {
00919 value[vv++] = *auth_line++;
00920 }
00921 }
00922 value[vv] = '\0';
00923 }
00924
00925 while (auth_line[0] != ',' && auth_line[0] != '\0') {
00926 auth_line++;
00927 }
00928 if (auth_line[0] != '\0') {
00929 auth_line++;
00930 }
00931
00932 if (!strcasecmp(key, "username"))
00933 resp->username = apr_pstrdup(r->pool, value);
00934 else if (!strcasecmp(key, "realm"))
00935 resp->realm = apr_pstrdup(r->pool, value);
00936 else if (!strcasecmp(key, "nonce"))
00937 resp->nonce = apr_pstrdup(r->pool, value);
00938 else if (!strcasecmp(key, "uri"))
00939 resp->uri = apr_pstrdup(r->pool, value);
00940 else if (!strcasecmp(key, "response"))
00941 resp->digest = apr_pstrdup(r->pool, value);
00942 else if (!strcasecmp(key, "algorithm"))
00943 resp->algorithm = apr_pstrdup(r->pool, value);
00944 else if (!strcasecmp(key, "cnonce"))
00945 resp->cnonce = apr_pstrdup(r->pool, value);
00946 else if (!strcasecmp(key, "opaque"))
00947 resp->opaque = apr_pstrdup(r->pool, value);
00948 else if (!strcasecmp(key, "qop"))
00949 resp->message_qop = apr_pstrdup(r->pool, value);
00950 else if (!strcasecmp(key, "nc"))
00951 resp->nonce_count = apr_pstrdup(r->pool, value);
00952 }
00953
00954 if (!resp->username || !resp->realm || !resp->nonce || !resp->uri
00955 || !resp->digest
00956 || (resp->message_qop && (!resp->cnonce || !resp->nonce_count))) {
00957 resp->auth_hdr_sts = INVALID;
00958 return !OK;
00959 }
00960
00961 if (resp->opaque) {
00962 resp->opaque_num = (unsigned long) strtol(resp->opaque, NULL, 16);
00963 }
00964
00965 resp->auth_hdr_sts = VALID;
00966 return OK;
00967 }
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981 static int parse_hdr_and_update_nc(request_rec *r)
00982 {
00983 digest_header_rec *resp;
00984 int res;
00985
00986 if (!ap_is_initial_req(r)) {
00987 return DECLINED;
00988 }
00989
00990 resp = apr_pcalloc(r->pool, sizeof(digest_header_rec));
00991 resp->raw_request_uri = r->unparsed_uri;
00992 resp->psd_request_uri = &r->parsed_uri;
00993 resp->needed_auth = 0;
00994 resp->method = r->method;
00995 ap_set_module_config(r->request_config, &auth_digest_module, resp);
00996
00997 res = get_digest_rec(r, resp);
00998 resp->client = get_client(resp->opaque_num, r);
00999 if (res == OK && resp->client) {
01000 resp->client->nonce_count++;
01001 }
01002
01003 return DECLINED;
01004 }
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014 static void gen_nonce_hash(char *hash, const char *timestr, const char *opaque,
01015 const server_rec *server,
01016 const digest_config_rec *conf)
01017 {
01018 const char *hex = "0123456789abcdef";
01019 unsigned char sha1[APR_SHA1_DIGESTSIZE];
01020 apr_sha1_ctx_t ctx;
01021 int idx;
01022
01023 memcpy(&ctx, &conf->nonce_ctx, sizeof(ctx));
01024
01025
01026
01027
01028
01029
01030 apr_sha1_update_binary(&ctx, (const unsigned char *) timestr, strlen(timestr));
01031 if (opaque) {
01032 apr_sha1_update_binary(&ctx, (const unsigned char *) opaque,
01033 strlen(opaque));
01034 }
01035 apr_sha1_final(sha1, &ctx);
01036
01037 for (idx=0; idx<APR_SHA1_DIGESTSIZE; idx++) {
01038 *hash++ = hex[sha1[idx] >> 4];
01039 *hash++ = hex[sha1[idx] & 0xF];
01040 }
01041
01042 *hash++ = '\0';
01043 }
01044
01045
01046
01047
01048 static const char *gen_nonce(apr_pool_t *p, apr_time_t