]> git.sesse.net Git - itkacl/blob - itkacl-2.1/itkacl.c
Switch to libunbound as the resolver, primarily to get DNSSEC support.
[itkacl] / itkacl-2.1 / itkacl.c
1 /*
2  * ITKACL control library.
3  *
4  * (C) 2004-2013 Steinar H. Gunderson
5  * GPL, v2.
6  */
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <ctype.h>
14 #include <unbound.h>
15
16 struct itkacl_config {
17         char nszone[256];
18 };
19
20 #define CONFIG_FILENAME "/etc/itkacl.conf"
21
22 static int itkacl_read_config(const char * const filename,
23                               struct itkacl_config *config,
24                               char *errmsg, size_t errmsg_size)
25 {
26         FILE *fp;
27         int lineno = 0;
28
29         strcpy(config->nszone, "");
30
31         fp = fopen(CONFIG_FILENAME, "r");
32         if (fp == NULL) {
33                 if (errmsg)
34                         snprintf(errmsg, errmsg_size, "%s: %s",
35                                  CONFIG_FILENAME, strerror(errno));
36                 return -1;
37         }
38
39         while (!feof(fp)) {
40                 char line[256], arg[256], *ptr;
41         
42                 if (fgets(line, sizeof(line), fp) == NULL) {
43                         break;
44                 }
45                 ++lineno;
46
47                 /* Remove trailing newlines and then comments. */
48                 ptr = strchr(line, '\n');
49                 if (ptr != NULL)
50                         *ptr = 0;
51
52                 ptr = strchr(line, '\r');
53                 if (ptr != NULL)
54                         *ptr = 0;
55
56                 ptr = strchr(line, '#');
57                 if (ptr != NULL)
58                         *ptr = 0;
59
60                 /* Remove trailing whitespace, if any. */
61                 ptr = line + strlen(line) - 1;
62                 while (ptr >= line && isspace(*ptr))
63                         *ptr-- = 0;
64
65                 /* Skip lines that now ended up blank. */
66                 if (line[0] == 0)
67                         continue;
68
69                 if (sscanf(line, "zone %255s", arg) == 1) {
70                         strcpy(config->nszone, arg);
71                         continue;
72                 }
73
74                 if (errmsg)
75                         snprintf(errmsg, errmsg_size, "%s: Could not parse line %d",
76                                  CONFIG_FILENAME, lineno);
77                 fclose(fp);
78                 return -1;
79         }
80
81         if (strlen(config->nszone) == 0) {
82                 if (errmsg)
83                         snprintf(errmsg, errmsg_size, "%s: Missing 'zone' directive",
84                                  CONFIG_FILENAME);
85                 fclose(fp);
86                 return -1;
87         }
88
89         fclose(fp);
90         return 0;
91 }
92
93 int itkacl_check(const char * const realm, const char * const user,
94                  char *errmsg, size_t errmsg_size)
95 {
96         struct itkacl_config config;
97         int ret, nxdomain;
98         const char *ptr;
99         char nszone[256];
100         char temp[256];
101         struct ub_ctx* ctx;
102         struct ub_result* result;
103
104         if (itkacl_read_config(CONFIG_FILENAME, &config, errmsg, errmsg_size) != 0) {
105                 return -1;
106         }
107
108         if (realm[0] != '/') {
109                 if (errmsg)
110                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (missing leading /)",
111                                 realm);
112                 return -1;
113         }
114         if (strlen(user) > 64) {
115                 if (errmsg)
116                         snprintf(errmsg, errmsg_size, "Invalid user '%s' (above 64 characters)",
117                                 user);
118                 return -1;
119         }
120         if (strlen(realm) > 64) {
121                 if (errmsg)
122                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (above 64 characters)",
123                                 realm);
124                 return -1;
125         }
126
127         /* check that the user name is valid */
128         ptr = user;
129         while (*ptr) {
130                 /* only allow [a-z0-9-] */
131                 if (!((*ptr >= 'a' && *ptr <= 'z') ||
132                       (*ptr >= '0' && *ptr <= '9') ||
133                        *ptr == '-')) {
134                         if (errmsg) {
135                                 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
136                                         realm);
137                         }
138                         return -1;
139                 }
140                 ++ptr;
141         }
142
143         /* traverse the realm entry by entry from the root,
144          * creating a DNS zone name as we go */
145         strcpy(nszone, config.nszone);
146         ptr = realm;
147         while (*ptr) {
148                 /* copy all characters to next / or end of string */
149                 char this_part[64];
150                 int i = 0;
151                 this_part[0] = 0;
152                 
153                 ++ptr;
154                 while (*ptr && *ptr != '/') {
155                         /* only allow [a-z0-9-] */
156                         if (!((*ptr >= 'a' && *ptr <= 'z') ||
157                               (*ptr >= '0' && *ptr <= '9') ||
158                                *ptr == '-')) {
159                                 if (errmsg) {
160                                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
161                                                 realm);
162                                 }
163                                 return -1;
164                         }
165                         this_part[i++] = *ptr++;
166                 }
167                 this_part[i] = 0;
168
169                 strcpy(temp, nszone);
170                 snprintf(nszone, 256, "%s.%s", this_part, temp);
171         }
172
173         /* finally, prepend the username */
174         strcpy(temp, nszone);
175         sprintf(nszone, "%s.%s", user, temp);
176
177         /* Create the DNS resolver context. */
178         ctx = ub_ctx_create();
179         if (ctx == NULL) {
180                 if (errmsg)
181                         snprintf(errmsg, errmsg_size, "Host name lookup failure: Could not create DNS context");
182                 return -1;
183         }
184
185         ret = ub_ctx_resolvconf(ctx, "/etc/resolv.conf");
186         if (ret != 0) {
187                 if (errmsg)
188                         snprintf(errmsg, errmsg_size,
189                                  "Host name lookup failure: Could not read /etc/resolv.conf "
190                                  "(resolver error: %s) (system error: %s)",
191                                  ub_strerror(ret), strerror(errno));
192                 ub_ctx_delete(ctx);
193                 return -1;
194         }
195
196         ret = ub_ctx_hosts(ctx, "/etc/hosts");
197         if (ret != 0) {
198                 if (errmsg)
199                         snprintf(errmsg, errmsg_size,
200                                  "Host name lookup failure: Could not read /etc/hosts "
201                                  "(resolver error: %s) (system error: %s)",
202                                  ub_strerror(ret), strerror(errno));
203                 ub_ctx_delete(ctx);
204                 return -1;
205         }
206
207         /* Do the actual DNS lookup (TYPE A, CLASS IN). */
208         ret = ub_resolve(ctx, nszone, 1, 1, &result);
209         if (ret != 0) {
210                 if (errmsg)
211                         snprintf(errmsg, errmsg_size, "Host name lookup failure: %s",
212                                  ub_strerror(ret));
213                 ub_ctx_delete(ctx);
214                 return -1;
215         }
216
217         nxdomain = result->nxdomain;
218
219         ub_resolve_free(result);
220         ub_ctx_delete(ctx);
221
222         if (nxdomain)
223                 return 1;
224         else
225                 return 0;
226 }