From ff76efc3e5e1b0e4ca3b10b7402406f619509bba Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 21 Apr 2004 17:37:06 +0000 Subject: Initial Import --- daemon/misc.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 daemon/misc.c (limited to 'daemon/misc.c') diff --git a/daemon/misc.c b/daemon/misc.c new file mode 100644 index 0000000..7091bfa --- /dev/null +++ b/daemon/misc.c @@ -0,0 +1,621 @@ + +#include "usuals.h" +#include "httpauthd.h" +#include "md5.h" + +#include +#include + +extern int g_debugging; +extern int g_daemonized; +extern pthread_mutex_t g_mutex; + + +/* ----------------------------------------------------------------------- + * Error Handling + */ + +const char kMsgDelimiter[] = ": "; +#define MAX_MSGLEN 128 + +void ha_messagex(int level, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + + /* Either to syslog or stderr */ + if(g_daemonized) + vsyslog(level, msg, ap); + + else + { + if(g_debugging || level > LOG_INFO) + vwarnx(msg, ap); + } + + va_end(ap); +} + +void ha_message(int level, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + + /* Either to syslog or stderr */ + if(g_daemonized) + { + char* m = (char*)alloca(strlen(msg) + MAX_MSGLEN + sizeof(kMsgDelimiter)); + + if(m) + { + strcpy(m, msg); + strcat(m, kMsgDelimiter); + strerror_r(errno, m + strlen(m), MAX_MSGLEN); + } + + vsyslog(LOG_ERR, m ? m : msg, ap); + } + else + { + if(g_debugging || level > LOG_INFO) + vwarnx(msg, ap); + } + + va_end(ap); +} + + +/* ----------------------------------------------------------------------- + * Header Functionality + */ + +ha_header_t* ha_findheader(ha_request_t* req, const char* name) +{ + int i; + + for(i = 0; i < MAX_HEADERS; i++) + { + if(req->headers[i].name) + { + if(strcasecmp(req->headers[i].name, name) == 0) + return &(req->headers[i]); + } + } + + return NULL; +} + +const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix) +{ + int i, l; + + for(i = 0; i < MAX_HEADERS; i++) + { + if(req->headers[i].name) + { + if(strcasecmp(req->headers[i].name, name) == 0) + { + if(!prefix) + return req->headers[i].data; + + l = strlen(prefix) + + if(strncasecmp(prefix, req->headers[i].value, l) == 0) + return req->headers[i].data + l; + } + } + } + + return NULL; +} + +void ha_addheader(ha_response_t* resp, const char* name, const char* data) +{ + int i = 0; + for(i = 0; i < MAX_HEADERS; i++) + { + if(!(resp->headers[i].name)) + { + resp->headers[i].name = name; + resp->headers[i].data = data; + return; + } + } + + ha_messagex(LOG_ERR, "too many headers in response. discarding '%s'", name); +} + + +/* ----------------------------------------------------------------------- + * Locking + */ + +void ha_lock(pthread_mutex_t* mtx) +{ + int r = pthread_mutex_lock(mtx ? mtx : &g_mutex); + if(r != 0) + { + errno = r; + ha_message(LOG_CRIT, "threading problem. couldn't lock mutex"); + } +} + +void ha_unlock(pthread_mutex_t* mtx) +{ + int r = pthread_mutex_unlock(mtx ? mtx : &g_mutex); + if(r != 0) + { + errno = r; + ha_message(LOG_CRIT, "threading problem. couldn't unlock mutex"); + } +} + + +/* ----------------------------------------------------------------------- + * Configuration + */ + +int ha_confbool(const char* name, const char* conf, int* value) +{ + if(value == NULL || + value[0] == 0 || + strcasecmp(conf, "0") == 0 || + strcasecmp(conf, "no") == 0 || + strcasecmp(conf, "false") == 0 || + strcasecmp(conf, "f") == 0 || + strcasecmp(conf, "off")) + { + *value = 0; + return HA_OK; + } + + if(strcasecmp(conf, "1") == 0 || + strcasecmp(conf, "yes") == 0 || + strcasecmp(conf, "true") == 0 || + strcasecmp(conf, "t") == 0 || + strcasecmp(conf, "on")) + { + *value = 1; + return HA_OK; + } + + ha_messagex(LOG_ERR, "invalid configuration value '%s': must be 'on' or 'off'.", name); + return HA_ERROR; +} + +int ha_confint(const char* name, const char* conf, int min, int max, int* value) +{ + const char* p; + errno = 0; + *value = strtol(conf, &p, 10); + + if(p != (name + strlen(name)) || errno == ERANGE || + (*value < min) || (*value > max)) + { + ha_messagex(LOG_ERR, "invalid configuration value '%s': must be a number between %d and %d", name, min, max); + return HA_ERROR; + } + + return HA_OK; +} + + +/* ----------------------------------------------------------------------- + * Hash Stuff + */ + +void ha_md5string(const char* data, unsigned char* hash) +{ + struct MD5Context md5; + MD5Init(&md5); + MD5Update(&md5, data, strlen(data)); + MD5Final(hash, &md5); +} + + +/* ----------------------------------------------------------------------- + * Client Authentication Functions + */ + +int ha_parsebasic(char* header, ha_buffer_t* buf, ha_basic_header_t* rec) +{ + char* t; + + memset(rec, 0, sizeof(*rec)); + + /* Trim the white space */ + while(*header && isspace(*header)) + header++; + + /* + * Authorization header is in this format: + * + * "Basic " B64(user ":" password) + */ + ha_bufnext(buf); + ha_bufdec64(buf, header); + + header = ha_bufdata(buf); + + if(!header) + return HA_ERROR; + + + /* We have a cache key at this point so hash it */ + ha_md5string(header, rec->key); + + + /* Parse the user. We need it in any case */ + t = strchr(header, ':'); + if(t != NULL) + { + /* Break the string in half */ + *t = 0; + + rec->user = header; + rec->password = t + 1; + } + + return HA_OK; +} + + +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +int ha_parsedigest(char* header, ha_buffer_t* buf, ha_digest_header_t* rec) +{ + /* + * This function destroys the contents of header by + * terminating strings in it all over the place. + */ + + char next; + char* key; + char* value; + + ha_bufnext(buf); + ha_bufcat(buf, header, NULL); + + header = ha_bufdata(buf); + + if(!header) + return HA_ERROR; + + memset(rec, 0, sizeof(rec)); + + while(header[0]) + { + /* find key */ + + while(header[0] && isspace(header[0])) + header++; + + key = header; + + while(header[0] && header[0] != '=' && header[0] != ',' && + !isspace(header[0])) + header++; + + /* null terminate and move on */ + next = header[0]; + header[0] = '\0'; + header++; + + if(!next) + break; + + if(isspace(t)) + { + while(header[0] && isspace(header[0])) + header++; + + next = header[0]; + } + + /* find value */ + + if(next == '=') + { + header++; + + while(header[0] && isspace(header[0])) + header++; + + vv = 0; + if(header[0] == '\"') /* quoted string */ + { + header++; + value = header; + + while(header[0] && header[0] != '\"') + header++; + + header[0] = 0; + header++; + } + + else /* token */ + { + value = header; + + while(header[0] && header[0] != ',' && !isspace(header[0])) + header++; + + header[0] = 0; + header++; + } + + while(header[0] && header[0] != ',') + header++; + + if(header[0]) + header++; + + if(!strcasecmp(key, "username")) + rec->username = value; + else if(!strcasecmp(key, "realm")) + rec->realm = value; + else if (!strcasecmp(key, "nonce")) + rec->nonce = value; + else if (!strcasecmp(key, "uri")) + rec->uri = value; + else if (!strcasecmp(key, "response")) + rec->digest = value; + else if (!strcasecmp(key, "algorithm")) + rec->algorithm = value; + else if (!strcasecmp(key, "cnonce")) + rec->cnonce = value; + else if (!strcasecmp(key, "opaque")) + rec->opaque = value; + else if (!strcasecmp(key, "qop")) + rec->qop = value; + else if (!strcasecmp(key, "nc")) + rec->nc = value; + } + } + + if(rec->nonce) + ha_md5string(rec->nonce, rec->key); + + return HA_OK; +} + +int ha_digestcheck(const char* realm, const char* method, const char* uri, + ha_buffer_t* buf, ha_digest_header_t* dg, ha_digest_record_t* rec) +{ + unsigned char hash[MD5_LEN]; + struct MD5Context md5; + const char* digest; + const char* t; + + /* Check for digest */ + if(!dg->digest || !dg->digest[0]) + { + ha_messagex(LOG_WARNING, "digest response missing digest"); + return HA_FALSE; + } + + /* Username */ + if(!dg->username || !dg->username[0] || + ha_md5strcmp(dg->username, rec->userhash) != 0) + { + ha_messagex(LOG_WARNING, "digest response missing username"); + return HA_FALSE; + } + + /* The realm */ + if(!dg->realm || strcmp(dg->realm, realm) != 0) + { + ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'", + dg->realm ? dg->realm : ""); + return HA_FALSE; + } + + /* Components in the new RFC */ + if(dg->qop) + { + /* + * We only support 'auth' qop. We don't have access to the data + * and wouldn't be able to support anything else. + */ + if(strcmp(dg->qop, "auth") != 0) + { + ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported qop: '%s'", + dg->qop ? dg->qop : ""); + return HA_FALSE; + } + + /* The nonce */ + if(!dg->cnonce || !dg->cnonce[0]) + { + ha_messagex(LOG_WARNING, "digest response is missing cnonce value"); + return HA_FALSE; + } + + /* The nonce count */ + if(!dg->nc || !dg->nc[0]) + { + ha_messagex(LOG_WARNING, "digest response is missing nc value"); + return HA_FALSE; + } + + /* Validate the nc */ + else + { + char* e; + long nc = strtol(dg->nc, &e, 10); + + if(e != (dg->nc + strlen(e)) || nc != rec->nc) + { + ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s", + dg->nc); + return HA_FALSE; + } + } + } + + /* The algorithm */ + if(dg->algorithm && strcasecmp(dg->algorithm, "MD5") != 0) + { + ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported algorithm: '%s'", + dg->algorithm ? dg->algorithm : ""); + return HA_FALSE; + } + + /* Request URI */ + if(!dg->uri) + { + ha_messagex(LOG_WARNING, "digest response is missing uri"); + return HA_FALSE; + } + + if(strcmp(dg->uri, uri) != 0) + { + ha_uri_t d_uri; + ha_uri_t s_uri; + + if(ha_uriparse(dg->uri, &d_uri) == HA_ERROR) + { + ha_messagex(LOG_WARNING, "digest response constains invalid uri: %s", dg->uri); + return HA_FALSE; + } + + if(ha_uriparse(uri, &s_uri) == HA_ERROR) + { + ha_messagex(LOG_ERR, "server sent us an invalid uri"); + return HA_ERROR; + } + + if(ha_uricmp(&d_uri, &s_uri) != 0) + { + ha_messagex(LOG_WARNING, "digest response contains wrong uri: %s " + "(should be %s)", dg->uri, uri); + return HA_ERROR; + } + } + + /* + * nonce: should have already been validated by the caller who + * found this nice rec structure to pass us. + * + * opaque: We also don't use opaque. The caller should have validated it + * if it's used there. + */ + + /* + * Now we validate the digest response + */ + + /* Encode ha1 */ + ha_bufnext(buf); + ha_bufenchex(buf, rec->ha1, MD5_LEN); + + if((t = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + /* Encode ha2 */ + MD5Init(&md5); + MD5Update(&md5, method, strlen(method)); + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->uri, strlen(dg->uri)); + MD5Final(hash, &md5); + + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + + /* Old style digest (RFC 2069) */ + if(!dg->qop) + { + MD5Init(&md5); + MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha1 */ + MD5Final(hash, &md5); + } + + /* New style 'auth' digest (RFC 2617) */ + else + { + MD5Init(&md5); + MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nc, strlen(dg->nc)); /* nc */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->qop, strlen(dg->qop)); /* qop */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */ + MD5Final(hash, &md5); + } + + /* Encode the digest */ + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if((digest = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + if(strcasecmp(dg->digest, digest) != 0) + { + ha_messagex(LOG_WARNING, "digest authentication failed for user: %s", dg->username); + return HA_FALSE; + } + + return HA_OK; +} + +const char* ha_digestrespond(ha_buffer_t* buf, ha_digest_header_t* dg, + ha_digest_rec_t* rec) +{ + unsigned char hash[MD5_LEN]; + struct MD5Context md5; + const char* t; + const char* digest; + + /* Encode ha1 */ + ha_bufnext(buf); + ha_bufenchex(buf, rec->ha1, MD5_LEN); + + if((t = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + /* Encode ha2 */ + MD5Init(&md5); + MD5Update(&md5, method, strlen(method)); + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->uri, strlen(dg->uri)); + MD5Final(hash, &md5); + + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if(!ha_bufdata(buf)) + return HA_ERROR; +} + + +char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf); +int ha_uriparse(const char* str, ha_uri_t* uri); -- cgit v1.2.3