+/*
+ * itkacl control library, version 0.1
+ *
+ * (C) 2004-2009 Steinar H. Gunderson
+ * GPL, v2.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#define BASE_ZONE "itkacl.samfundet.no"
+
+int itkacl_check(const char * const realm, const char * const user,
+ char *errmsg, size_t errmsg_size)
+{
+ struct hostent he, *he_ptr;
+ int ret, host_errno;
+ const char *ptr;
+ char nszone[256] = BASE_ZONE;
+ char temp[256], ns_temp[1024];
+
+ if (realm[0] != '/') {
+ if (errmsg)
+ snprintf(errmsg, errmsg_size, "Invalid realm '%s' (missing leading /)",
+ realm);
+ return -1;
+ }
+ if (strlen(user) > 64) {
+ if (errmsg)
+ snprintf(errmsg, errmsg_size, "Invalid user '%s' (above 64 characters)",
+ user);
+ return -1;
+ }
+ if (strlen(realm) > 64) {
+ if (errmsg)
+ snprintf(errmsg, errmsg_size, "Invalid realm '%s' (above 64 characters)",
+ realm);
+ return -1;
+ }
+
+ /* check that the user name is valid */
+ ptr = user;
+ while (*ptr) {
+ /* only allow [a-z0-9-] */
+ if (!((*ptr >= 'a' && *ptr <= 'z') ||
+ (*ptr >= '0' && *ptr <= '9') ||
+ *ptr == '-')) {
+ if (errmsg) {
+ snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
+ realm);
+ }
+ return -1;
+ }
+ ++ptr;
+ }
+
+ /* traverse the realm entry by entry from the root,
+ * creating a DNS zone name as we go */
+ ptr = realm;
+ while (*ptr) {
+ /* copy all characters to next / or end of string */
+ char this_part[64];
+ int i = 0;
+ this_part[0] = 0;
+
+ ++ptr;
+ while (*ptr && *ptr != '/') {
+ /* only allow [a-z0-9-] */
+ if (!((*ptr >= 'a' && *ptr <= 'z') ||
+ (*ptr >= '0' && *ptr <= '9') ||
+ *ptr == '-')) {
+ if (errmsg) {
+ snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
+ realm);
+ }
+ return -1;
+ }
+ this_part[i++] = *ptr++;
+ }
+ this_part[i] = 0;
+
+ strcpy(temp, nszone);
+ snprintf(nszone, 256, "%s.%s", this_part, temp);
+ }
+
+ /* finally, prepend the username */
+ strcpy(temp, nszone);
+ sprintf(nszone, "%s.%s", user, temp);
+
+ ret = gethostbyname_r(nszone, &he, ns_temp, 1024, &he_ptr, &host_errno);
+
+ /*
+ * The man page for gethostbyname_r() specifies ret != 0 on failure, but
+ * that seemingly does not include HOST_NOT_FOUND failure.
+ */
+ if (he_ptr == NULL) {
+ // Not found => no access, but no error either.
+ if (host_errno == HOST_NOT_FOUND) {
+ return 1;
+ }
+
+ switch (host_errno) {
+ case TRY_AGAIN:
+ snprintf(errmsg, errmsg_size, "Host name lookup failure");
+ break;
+ case NO_RECOVERY:
+ snprintf(errmsg, errmsg_size, "Unknown server error");
+ break;
+ case NO_ADDRESS:
+ snprintf(errmsg, errmsg_size, "No address associated with name");
+ break;
+ default:
+ snprintf(errmsg, errmsg_size, "Unknown DNS error %d", host_errno);
+ }
+ return -1;
+ }
+
+ // The lookup succeeded, so we're good.
+ return 0;
+}