summaryrefslogtreecommitdiff
path: root/daemon/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/misc.c')
-rw-r--r--daemon/misc.c599
1 files changed, 270 insertions, 329 deletions
diff --git a/daemon/misc.c b/daemon/misc.c
index 7091bfa..b585d8b 100644
--- a/daemon/misc.c
+++ b/daemon/misc.c
@@ -5,6 +5,8 @@
#include <syslog.h>
#include <err.h>
+#include <stdio.h>
+#include <fcntl.h>
extern int g_debugging;
extern int g_daemonized;
@@ -98,9 +100,9 @@ const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix
if(!prefix)
return req->headers[i].data;
- l = strlen(prefix)
+ l = strlen(prefix);
- if(strncasecmp(prefix, req->headers[i].value, l) == 0)
+ if(strncasecmp(prefix, req->headers[i].data, l) == 0)
return req->headers[i].data + l;
}
}
@@ -185,7 +187,7 @@ int ha_confbool(const char* name, const char* conf, int* value)
int ha_confint(const char* name, const char* conf, int min, int max, int* value)
{
- const char* p;
+ char* p;
errno = 0;
*value = strtol(conf, &p, 10);
@@ -201,421 +203,360 @@ int ha_confint(const char* name, const char* conf, int min, int max, int* value)
/* -----------------------------------------------------------------------
- * 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* ha_uriformat(ha_buffer_t* buf, const ha_uri_t* uri)
{
- char* t;
-
- memset(rec, 0, sizeof(*rec));
+ /* This creates a new block */
+ ha_bufcpy(buf, "");
- /* 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);
+ if(uri->host)
+ {
+ const char* l = "";
+ const char* r = "";
- header = ha_bufdata(buf);
+ ha_bufmcat(buf, uri->scheme ? uri->scheme : "http",
+ "://", NULL);
- if(!header)
- return HA_ERROR;
+ if(uri->user)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, uri->user,
+ uri->pw ? ":" : "",
+ uri->pw ? uri->pw : "",
+ "@", NULL);
+ }
+ if(strchr(uri->host, ':')) /* v6 ip */
+ {
+ l = "[";
+ r = "]";
+ }
- /* We have a cache key at this point so hash it */
- ha_md5string(header, rec->key);
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, l, uri->host, r, NULL);
+ }
+ if(uri->path)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "/", uri->path);
+ }
- /* Parse the user. We need it in any case */
- t = strchr(header, ':');
- if(t != NULL)
+ if(uri->query)
{
- /* Break the string in half */
- *t = 0;
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "?", uri->query);
+ }
- rec->user = header;
- rec->password = t + 1;
+ if(uri->fragment)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "#", uri->fragment);
}
- return HA_OK;
+ return ha_bufdata(buf);
}
+int ha_uriparse(ha_buffer_t* buf, const char* suri, ha_uri_t* uri)
+{
+ char* s;
+ char* s1;
+ char* hostinfo;
+ char* endstr;
+ char* str;
+ int port;
+ int v6_offset1 = 0;
+
+ /* Copy the memory */
+ str = ha_bufcpy(buf, suri);
+
+ if(!str)
+ return HA_ERROR;
-/*
- * 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.
- */
+ /* Initialize the structure */
+ memset(uri, 0, sizeof(*uri));
-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.
+ * We assume the processor has a branch predictor like most --
+ * it assumes forward branches are untaken and backwards are taken. That's
+ * the reason for the gotos. -djg
*/
- 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])
+ if(str[0] == '/')
{
- /* find key */
-
- while(header[0] && isspace(header[0]))
- header++;
-
- key = header;
-
- while(header[0] && header[0] != '=' && header[0] != ',' &&
- !isspace(header[0]))
- header++;
+deal_with_path:
+ *str == 0;
+ ++str;
- /* 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++;
+ /*
+ * we expect uri to point to first character of path ... remember
+ * that the path could be empty -- http://foobar?query for example
+ */
+ s = str;
+ while(*s && *s != '?' && *s != '#')
+ ++s;
- next = header[0];
- }
+ if(s != str)
+ uri->path = str;
- /* find value */
+ if(*s == 0)
+ return HA_OK;
- if(next == '=')
+ if(*s == '?')
{
- header++;
-
- while(header[0] && isspace(header[0]))
- header++;
+ *s = 0;
+ ++s;
+ uri->query = s;
- vv = 0;
- if(header[0] == '\"') /* quoted string */
+ s1 = strchr(s, '#');
+ if(s1)
{
- header++;
- value = header;
-
- while(header[0] && header[0] != '\"')
- header++;
-
- header[0] = 0;
- header++;
+ *s1 = 0;
+ ++s1;
+ uri->fragment = s1;
}
- else /* token */
- {
- value = header;
-
- while(header[0] && header[0] != ',' && !isspace(header[0]))
- header++;
+ return HA_OK;
+ }
- header[0] = 0;
- header++;
- }
+ *s = 0;
+ ++s;
- 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;
- }
+ /* otherwise it's a fragment */
+ uri->fragment = s;
+ return HA_OK;
}
- if(rec->nonce)
- ha_md5string(rec->nonce, rec->key);
+ /* find the scheme: */
+ s = str;
- return HA_OK;
-}
+ while(*s && *s != ':' && *s != '/' && *s != '?' && *s != '#')
+ s++;
-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;
+ /* scheme must be non-empty and followed by :// */
+ if(s == str || s[0] != ':' || s[1] != '/' || s[2] != '/')
+ goto deal_with_path; /* backwards predicted taken! */
- /* Check for digest */
- if(!dg->digest || !dg->digest[0])
- {
- ha_messagex(LOG_WARNING, "digest response missing digest");
- return HA_FALSE;
- }
+ uri->scheme = str;
+ *s = 0;
+ s += 3;
- /* 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;
- }
+ hostinfo = s;
+
+ while(*s && *s != '/' && *s != '?' && *s != '#')
+ ++s;
- /* The realm */
- if(!dg->realm || strcmp(dg->realm, realm) != 0)
+ str = s; /* whatever follows hostinfo is start of uri */
+
+ /*
+ * If there's a username:password@host:port, the @ we want is the last @...
+ * too bad there's no memrchr()... For the C purists, note that hostinfo
+ * is definately not the first character of the original uri so therefore
+ * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
+ */
+ do
{
- ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'",
- dg->realm ? dg->realm : "");
- return HA_FALSE;
+ --s;
}
+ while(s >= hostinfo && *s != '@');
- /* Components in the new RFC */
- if(dg->qop)
+ if(s < hostinfo)
{
+ /* again we want the common case to be fall through */
+deal_with_host:
/*
- * We only support 'auth' qop. We don't have access to the data
- * and wouldn't be able to support anything else.
+ * We expect hostinfo to point to the first character of
+ * the hostname. If there's a port it is the first colon,
+ * except with IPv6.
*/
- 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])
+ if(*hostinfo == '[')
{
- ha_messagex(LOG_WARNING, "digest response is missing cnonce value");
- return HA_FALSE;
- }
+ v6_offset1 = 1;
- /* The nonce count */
- if(!dg->nc || !dg->nc[0])
- {
- ha_messagex(LOG_WARNING, "digest response is missing nc value");
- return HA_FALSE;
+ s = str;
+ for(;;)
+ {
+ --s;
+
+ if(s < hostinfo)
+ {
+ s = NULL; /* no port */
+ break;
+ }
+
+ else if(*s == ']')
+ {
+ *s = 0;
+ s = NULL; /* no port */;
+ break;
+ }
+
+ else if(*s == ':')
+ {
+ *s = 0;
+ ++s; /* found a port */
+ break;
+ }
+ }
}
- /* Validate the nc */
else
{
- char* e;
- long nc = strtol(dg->nc, &e, 10);
-
- if(e != (dg->nc + strlen(e)) || nc != rec->nc)
+ s = memchr(hostinfo, ':', str - hostinfo);
+ if(s)
{
- ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s",
- dg->nc);
- return HA_FALSE;
+ *s = 0;
+ ++s;
}
}
- }
-
- /* 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;
+ uri->host = hostinfo + v6_offset1;
- if(ha_uriparse(dg->uri, &d_uri) == HA_ERROR)
+ if(s == NULL)
{
- ha_messagex(LOG_WARNING, "digest response constains invalid uri: %s", dg->uri);
- return HA_FALSE;
+ /* we expect the common case to have no port */
+ goto deal_with_path;
}
- if(ha_uriparse(uri, &s_uri) == HA_ERROR)
+ if(str != s)
{
- ha_messagex(LOG_ERR, "server sent us an invalid uri");
- return HA_ERROR;
+ port = strtol(s, &endstr, 10);
+ uri->port = port;
+ if(*endstr != '\0')
+ return HA_FALSE;
}
- 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;
- }
+ goto deal_with_path;
}
- /*
- * 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.
- */
+ /* Remove the @ symbol */
+ *s = 0;
- /*
- * Now we validate the digest response
- */
+ /* first colon delimits username:password */
+ s1 = memchr(hostinfo, ':', s - hostinfo);
+ if(s1)
+ {
+ *s1 = 0;
+ ++s1;
- /* Encode ha1 */
- ha_bufnext(buf);
- ha_bufenchex(buf, rec->ha1, MD5_LEN);
+ uri->user = hostinfo;
+ uri->pw = s1;
+ }
+ else
+ {
+ str = hostinfo;
+ }
- if((t = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ hostinfo = s + 1;
+ goto deal_with_host;
+}
- /* Encode ha2 */
- MD5Init(&md5);
- MD5Update(&md5, method, strlen(method));
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->uri, strlen(dg->uri));
- MD5Final(hash, &md5);
+int uri_cmp_part_s(const char* s1, const char* s2, const char* def, int cs)
+{
+ if(def)
+ {
+ if(s1 && strcmp(def, s1) == 0)
+ s1 = NULL;
+ if(s2 && strcmp(def, s2) == 0)
+ s2 = NULL;
+ }
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+ if(!s1 && !s2)
+ return 0;
- if(!ha_bufdata(buf))
- return HA_ERROR;
+ if(!s1)
+ return -1;
+ if(!s2)
+ return 1;
- /* 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);
- }
+ return cs ? strcasecmp(s1, s2) : strcmp(s1, s2);
+}
- /* New style 'auth' digest (RFC 2617) */
- else
+int uri_cmp_part_n(int n1, int n2, int def)
+{
+ if(def)
{
- 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);
+ if(n1 == def)
+ n1 = 0;
+ if(n2 == def)
+ n2 = 0;
}
- /* Encode the digest */
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+ if(n1 == n2)
+ return 0;
- if((digest = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ else if(n1 < n2)
+ return -1;
- if(strcasecmp(dg->digest, digest) != 0)
- {
- ha_messagex(LOG_WARNING, "digest authentication failed for user: %s", dg->username);
- return HA_FALSE;
- }
+ else if(n2 > n1)
+ return 1;
- return HA_OK;
+ /* Not reached */
+ ASSERT(0);
}
-const char* ha_digestrespond(ha_buffer_t* buf, ha_digest_header_t* dg,
- ha_digest_rec_t* rec)
+int ha_uricmp(ha_uri_t* one, ha_uri_t* two)
{
- unsigned char hash[MD5_LEN];
- struct MD5Context md5;
- const char* t;
- const char* digest;
+ int r;
- /* Encode ha1 */
- ha_bufnext(buf);
- ha_bufenchex(buf, rec->ha1, MD5_LEN);
+ /* We don't compare user or password */
- if((t = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ /* The scheme */
+ if((r = uri_cmp_part_s(one->scheme, two->scheme, "http", 0)) != 0 ||
+ (r = uri_cmp_part_s(one->host, two->host, NULL, 1)) != 0 != 0 ||
+ (r = uri_cmp_part_n(one->port, two->port, 80)) != 0 ||
+ (r = uri_cmp_part_s(one->path, two->path, NULL, 0)) != 0 ||
+ (r = uri_cmp_part_s(one->query, two->query, NULL, 0)) != 0 ||
+ (r = uri_cmp_part_s(one->fragment, two->fragment, NULL, 0)))
+ return r;
- /* Encode ha2 */
- MD5Init(&md5);
- MD5Update(&md5, method, strlen(method));
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->uri, strlen(dg->uri));
- MD5Final(hash, &md5);
+ return 0;
+}
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+int ha_genrandom(unsigned char* data, size_t len)
+{
+ int r, dd;
- if(!ha_bufdata(buf))
+ dd = open("/dev/urandom", O_RDONLY);
+ if(dd == -1)
+ {
+ ha_message(LOG_ERR, "couldn't open /dev/urandom");
return HA_ERROR;
-}
+ }
+
+ for(;;)
+ {
+ r = read(dd, data, len);
+
+ if(r == -1)
+ {
+ switch(errno)
+ {
+ case EINTR:
+ case EAGAIN:
+ continue;
+
+ default:
+ ha_message(LOG_ERR, "couldn't read random bytes from /dev/urandom");
+ break;
+ }
+ break;
+ }
+
+ else if(r >= 0)
+ {
+ if(r >= len)
+ break;
-char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf);
-int ha_uriparse(const char* str, ha_uri_t* uri);
+ len -= r;
+ data += r;
+ }
+ }
+
+ close(dd);
+ return r == -1 ? HA_ERROR : HA_OK;
+}