summaryrefslogtreecommitdiff
path: root/daemon/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/misc.c')
-rw-r--r--daemon/misc.c621
1 files changed, 621 insertions, 0 deletions
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 <syslog.h>
+#include <err.h>
+
+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);