Fix a bug where max_updates_per_transaction would not be respected.
[itkacl] / itkacl-2.1 / itkacl.c
index c50654ce2a97b39575c17e954d49db244848a8b5..2795cf4d6885899058ab740cb436997a9276db5d 100644 (file)
 #include <unistd.h>
 #include <errno.h>
 #include <ctype.h>
-#include <netdb.h>
+#include <unbound.h>
 
 struct itkacl_config {
        char nszone[256];
+       int require_dnssec;
+       char dnssec_public_key[256];
 };
 
 #define CONFIG_FILENAME "/etc/itkacl.conf"
@@ -26,7 +28,10 @@ static int itkacl_read_config(const char * const filename,
        FILE *fp;
        int lineno = 0;
 
+       /* Defaults. */
        strcpy(config->nszone, "");
+       config->require_dnssec = 0;
+       strcpy(config->dnssec_public_key, "");
 
        fp = fopen(CONFIG_FILENAME, "r");
        if (fp == NULL) {
@@ -70,6 +75,14 @@ static int itkacl_read_config(const char * const filename,
                        strcpy(config->nszone, arg);
                        continue;
                }
+               if (strcmp(line, "require-dnssec") == 0) {
+                       config->require_dnssec = 1;
+                       continue;
+               }
+               if (sscanf(line, "dnssec-public-key %255s", arg) == 1) {
+                       strcpy(config->dnssec_public_key, arg);
+                       continue;
+               }
 
                if (errmsg)
                        snprintf(errmsg, errmsg_size, "%s: Could not parse line %d",
@@ -94,11 +107,12 @@ int itkacl_check(const char * const realm, const char * const user,
                 char *errmsg, size_t errmsg_size)
 {
        struct itkacl_config config;
-       struct hostent he, *he_ptr;
-       int ret, host_errno;
+       int ret, nxdomain;
        const char *ptr;
        char nszone[256];
-       char temp[256], ns_temp[1024];
+       char temp[256];
+       struct ub_ctx* ctx;
+       struct ub_result* result;
 
        if (itkacl_read_config(CONFIG_FILENAME, &config, errmsg, errmsg_size) != 0) {
                return -1;
@@ -173,34 +187,85 @@ int itkacl_check(const char * const realm, const char * const user,
        strcpy(temp, nszone);
        sprintf(nszone, "%s.%s", user, temp);
 
-       ret = gethostbyname_r(nszone, &he, ns_temp, 1024, &he_ptr, &host_errno);
+       /* Create the DNS resolver context. */
+       ctx = ub_ctx_create();
+       if (ctx == NULL) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size, "Host name lookup failure: Could not create DNS context");
+               return -1;
+       }
 
-       /*
-        * 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;
-               }
+       ret = ub_ctx_resolvconf(ctx, "/etc/resolv.conf");
+       if (ret != 0) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size,
+                                "Host name lookup failure: Could not read /etc/resolv.conf "
+                                "(resolver error: %s) (system error: %s)",
+                                ub_strerror(ret), strerror(errno));
+               ub_ctx_delete(ctx);
+               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);
+       ret = ub_ctx_hosts(ctx, "/etc/hosts");
+       if (ret != 0) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size,
+                                "Host name lookup failure: Could not read /etc/hosts "
+                                "(resolver error: %s) (system error: %s)",
+                                ub_strerror(ret), strerror(errno));
+               ub_ctx_delete(ctx);
+               return -1;
+       }
+
+       if (strlen(config.dnssec_public_key) != 0) {
+               ret = ub_ctx_add_ta_file(ctx, config.dnssec_public_key);
+               if (ret != 0) {
+                       if (errmsg)
+                               snprintf(errmsg, errmsg_size,
+                                        "Host name lookup failure: Error adding keys from %s "
+                                        "(resolver error: %s) (system error: %s)",
+                                        config.dnssec_public_key,
+                                        ub_strerror(ret), strerror(errno));
+                       ub_ctx_delete(ctx);
+                       return -1;
                }
+       }
+
+       /* Do the actual DNS lookup (TYPE A, CLASS IN). */
+       ret = ub_resolve(ctx, nszone, 1, 1, &result);
+       if (ret != 0) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size, "Host name lookup failure: %s",
+                                ub_strerror(ret));
+               ub_ctx_delete(ctx);
                return -1;
        }
 
-       // The lookup succeeded, so we're good.
-       return 0;
+       /* Verify DNSSEC. */
+       if (result->bogus) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size,
+                                "Host name lookup failure: Bogus DNSSEC result (security failure)");
+               ub_resolve_free(result);
+               ub_ctx_delete(ctx);
+               return -1;
+       }
+       if (config.require_dnssec && !result->secure) {
+               if (errmsg)
+                       snprintf(errmsg, errmsg_size,
+                                "Host name lookup failure: Result was not secured with DNSSEC");
+               ub_resolve_free(result);
+               ub_ctx_delete(ctx);
+               return -1;
+       }
+
+       nxdomain = result->nxdomain;
+
+       ub_resolve_free(result);
+       ub_ctx_delete(ctx);
+
+       if (nxdomain)
+               return 1;
+       else
+               return 0;
 }