X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=itkacl-2.1%2Fitkacl.c;fp=itkacl-2.1%2Fitkacl.c;h=1ebd3a096fec2c24299b3b2f97a24d114ac4b287;hb=66f114e2e5b127115cc127ea23865371f1bea90d;hp=0000000000000000000000000000000000000000;hpb=19417083de9c8b30ac8b590690783b11413e2c58;p=itkacl diff --git a/itkacl-2.1/itkacl.c b/itkacl-2.1/itkacl.c new file mode 100644 index 0000000..1ebd3a0 --- /dev/null +++ b/itkacl-2.1/itkacl.c @@ -0,0 +1,206 @@ +/* + * itkacl control library, version 0.1 + * + * (C) 2004-2009 Steinar H. Gunderson + * GPL, v2. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct itkacl_config { + char nszone[256]; +}; + +#define CONFIG_FILENAME "/etc/itkacl.conf" + +static int itkacl_read_config(const char * const filename, + struct itkacl_config *config, + char *errmsg, size_t errmsg_size) +{ + FILE *fp; + int lineno = 0; + + strcpy(config->nszone, ""); + + fp = fopen(CONFIG_FILENAME, "r"); + if (fp == NULL) { + if (errmsg) + snprintf(errmsg, errmsg_size, "%s: %s", + CONFIG_FILENAME, strerror(errno)); + return -1; + } + + while (!feof(fp)) { + char line[256], arg[256], *ptr; + + if (fgets(line, sizeof(line), fp) == NULL) { + break; + } + ++lineno; + + /* Remove trailing newlines and then comments. */ + ptr = strchr(line, '\n'); + if (ptr != NULL) + *ptr = 0; + + ptr = strchr(line, '\r'); + if (ptr != NULL) + *ptr = 0; + + ptr = strchr(line, '#'); + if (ptr != NULL) + *ptr = 0; + + /* Remove trailing whitespace, if any. */ + ptr = line + strlen(line) - 1; + while (ptr >= line && isspace(*ptr)) + *ptr-- = 0; + + /* Skip lines that now ended up blank. */ + if (line[0] == 0) + continue; + + if (sscanf(line, "zone %255s", arg) == 1) { + strcpy(config->nszone, arg); + continue; + } + + if (errmsg) + snprintf(errmsg, errmsg_size, "%s: Could not parse line %d", + CONFIG_FILENAME, lineno); + fclose(fp); + return -1; + } + + if (strlen(config->nszone) == 0) { + if (errmsg) + snprintf(errmsg, errmsg_size, "%s: Missing 'zone' directive", + CONFIG_FILENAME); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +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; + const char *ptr; + char nszone[256]; + char temp[256], ns_temp[1024]; + + if (itkacl_read_config(CONFIG_FILENAME, &config, errmsg, errmsg_size) != 0) { + return -1; + } + + 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 */ + strcpy(nszone, config.nszone); + 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; +}