2 * ITKACL control library.
4 * (C) 2004-2022 Steinar H. Gunderson
18 struct itkacl_config {
21 char dnssec_public_key[256];
25 struct itkacl_config config;
29 #define CONFIG_FILENAME "/etc/itkacl.conf"
31 static int itkacl_read_config(const char * const filename,
32 struct itkacl_config *config,
33 char *errmsg, size_t errmsg_size)
39 strcpy(config->nszone, "");
40 config->require_dnssec = 0;
41 strcpy(config->dnssec_public_key, "");
43 fp = fopen(CONFIG_FILENAME, "r");
46 snprintf(errmsg, errmsg_size, "%s: %s",
47 CONFIG_FILENAME, strerror(errno));
52 char line[256], arg[256], *ptr;
54 if (fgets(line, sizeof(line), fp) == NULL) {
59 /* Remove trailing newlines and then comments. */
60 ptr = strchr(line, '\n');
64 ptr = strchr(line, '\r');
68 ptr = strchr(line, '#');
72 /* Remove trailing whitespace, if any. */
73 ptr = line + strlen(line) - 1;
74 while (ptr >= line && isspace(*ptr))
77 /* Skip lines that now ended up blank. */
81 if (sscanf(line, "zone %255s", arg) == 1) {
82 strcpy(config->nszone, arg);
85 if (strcmp(line, "require-dnssec") == 0) {
86 config->require_dnssec = 1;
89 if (sscanf(line, "dnssec-public-key %255s", arg) == 1) {
90 strcpy(config->dnssec_public_key, arg);
95 snprintf(errmsg, errmsg_size, "%s: Could not parse line %d",
96 CONFIG_FILENAME, lineno);
101 if (strlen(config->nszone) == 0) {
103 snprintf(errmsg, errmsg_size, "%s: Missing 'zone' directive",
113 int itkacl_check(const char * const realm, const char * const user,
114 char *errmsg, size_t errmsg_size)
116 struct itkacl_ctx *ctx;
119 ctx = itkacl_create_ctx(errmsg, errmsg_size);
124 err = itkacl_check_with_ctx(ctx, realm, user, errmsg, errmsg_size);
126 itkacl_free_ctx(ctx);
131 struct itkacl_ctx *itkacl_create_ctx(char *errmsg, size_t errmsg_size)
133 struct itkacl_ctx *ctx;
136 ctx = (struct itkacl_ctx *)malloc(sizeof(struct itkacl_ctx));
139 snprintf(errmsg, errmsg_size, "Memory allocation failed");
143 if (itkacl_read_config(CONFIG_FILENAME, &ctx->config, errmsg, errmsg_size) != 0) {
148 /* Create the DNS resolver context. */
149 ctx->ubctx = ub_ctx_create();
150 if (ctx->ubctx == NULL) {
152 snprintf(errmsg, errmsg_size, "Host name lookup failure: Could not create DNS context");
157 ret = ub_ctx_resolvconf(ctx->ubctx, NULL);
160 snprintf(errmsg, errmsg_size,
161 "Host name lookup failure: Could not read resolv.conf "
162 "(resolver error: %s) (system error: %s)",
163 ub_strerror(ret), strerror(errno));
164 ub_ctx_delete(ctx->ubctx);
169 ret = ub_ctx_hosts(ctx->ubctx, NULL);
172 snprintf(errmsg, errmsg_size,
173 "Host name lookup failure: Could not read hosts file "
174 "(resolver error: %s) (system error: %s)",
175 ub_strerror(ret), strerror(errno));
176 ub_ctx_delete(ctx->ubctx);
181 if (strlen(ctx->config.dnssec_public_key) != 0) {
182 ret = ub_ctx_add_ta_file(ctx->ubctx, ctx->config.dnssec_public_key);
185 snprintf(errmsg, errmsg_size,
186 "Host name lookup failure: Error adding keys from %s "
187 "(resolver error: %s) (system error: %s)",
188 ctx->config.dnssec_public_key,
189 ub_strerror(ret), strerror(errno));
190 ub_ctx_delete(ctx->ubctx);
199 void itkacl_free_ctx(struct itkacl_ctx *ctx)
201 ub_ctx_delete(ctx->ubctx);
205 int itkacl_check_with_ctx(struct itkacl_ctx *ctx,
206 const char * const realm, const char * const user,
207 char *errmsg, size_t errmsg_size)
213 struct ub_result* result;
215 if (realm[0] != '/') {
217 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (missing leading /)",
221 if (strlen(user) > 64) {
223 snprintf(errmsg, errmsg_size, "Invalid user '%s' (above 64 characters)",
227 if (strlen(realm) > 64) {
229 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (above 64 characters)",
234 /* check that the user name is valid */
237 /* only allow [a-z0-9-] */
238 if (!((*ptr >= 'a' && *ptr <= 'z') ||
239 (*ptr >= '0' && *ptr <= '9') ||
242 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
250 /* traverse the realm entry by entry from the root,
251 * creating a DNS zone name as we go */
252 strcpy(nszone, ctx->config.nszone);
255 /* copy all characters to next / or end of string */
261 while (*ptr && *ptr != '/') {
262 /* only allow [a-z0-9-] */
263 if (!((*ptr >= 'a' && *ptr <= 'z') ||
264 (*ptr >= '0' && *ptr <= '9') ||
267 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
272 this_part[i++] = *ptr++;
276 strcpy(temp, nszone);
277 snprintf(nszone, 256, "%s.%s", this_part, temp);
280 /* finally, prepend the username */
281 strcpy(temp, nszone);
282 sprintf(nszone, "%s.%s", user, temp);
284 /* Do the actual DNS lookup (TYPE A, CLASS IN). */
285 ret = ub_resolve(ctx->ubctx, nszone, 1, 1, &result);
288 snprintf(errmsg, errmsg_size, "Host name lookup failure: %s",
296 snprintf(errmsg, errmsg_size,
297 "Host name lookup failure: Bogus DNSSEC result (security failure)");
298 ub_resolve_free(result);
301 if (ctx->config.require_dnssec && !result->secure) {
303 snprintf(errmsg, errmsg_size,
304 "Host name lookup failure: Result was not secured with DNSSEC");
305 ub_resolve_free(result);
309 nxdomain = result->nxdomain;
311 ub_resolve_free(result);