1ebd3a096fec2c24299b3b2f97a24d114ac4b287
[itkacl] / itkacl-2.1 / itkacl.c
1 /*
2  * itkacl control library, version 0.1
3  *
4  * (C) 2004-2009 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 <netdb.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         struct hostent he, *he_ptr;
98         int ret, host_errno;
99         const char *ptr;
100         char nszone[256];
101         char temp[256], ns_temp[1024];
102
103         if (itkacl_read_config(CONFIG_FILENAME, &config, errmsg, errmsg_size) != 0) {
104                 return -1;
105         }
106
107         if (realm[0] != '/') {
108                 if (errmsg)
109                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (missing leading /)",
110                                 realm);
111                 return -1;
112         }
113         if (strlen(user) > 64) {
114                 if (errmsg)
115                         snprintf(errmsg, errmsg_size, "Invalid user '%s' (above 64 characters)",
116                                 user);
117                 return -1;
118         }
119         if (strlen(realm) > 64) {
120                 if (errmsg)
121                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (above 64 characters)",
122                                 realm);
123                 return -1;
124         }
125
126         /* check that the user name is valid */
127         ptr = user;
128         while (*ptr) {
129                 /* only allow [a-z0-9-] */
130                 if (!((*ptr >= 'a' && *ptr <= 'z') ||
131                       (*ptr >= '0' && *ptr <= '9') ||
132                        *ptr == '-')) {
133                         if (errmsg) {
134                                 snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
135                                         realm);
136                         }
137                         return -1;
138                 }
139                 ++ptr;
140         }
141
142         /* traverse the realm entry by entry from the root,
143          * creating a DNS zone name as we go */
144         strcpy(nszone, config.nszone);
145         ptr = realm;
146         while (*ptr) {
147                 /* copy all characters to next / or end of string */
148                 char this_part[64];
149                 int i = 0;
150                 this_part[0] = 0;
151                 
152                 ++ptr;
153                 while (*ptr && *ptr != '/') {
154                         /* only allow [a-z0-9-] */
155                         if (!((*ptr >= 'a' && *ptr <= 'z') ||
156                               (*ptr >= '0' && *ptr <= '9') ||
157                                *ptr == '-')) {
158                                 if (errmsg) {
159                                         snprintf(errmsg, errmsg_size, "Invalid realm '%s' (illegal characters)",
160                                                 realm);
161                                 }
162                                 return -1;
163                         }
164                         this_part[i++] = *ptr++;
165                 }
166                 this_part[i] = 0;
167
168                 strcpy(temp, nszone);
169                 snprintf(nszone, 256, "%s.%s", this_part, temp);
170         }
171
172         /* finally, prepend the username */
173         strcpy(temp, nszone);
174         sprintf(nszone, "%s.%s", user, temp);
175
176         ret = gethostbyname_r(nszone, &he, ns_temp, 1024, &he_ptr, &host_errno);
177
178         /*
179          * The man page for gethostbyname_r() specifies ret != 0 on failure, but
180          * that seemingly does not include HOST_NOT_FOUND failure.
181          */
182         if (he_ptr == NULL) {
183                 // Not found => no access, but no error either.
184                 if (host_errno == HOST_NOT_FOUND) {
185                         return 1;
186                 }
187
188                 switch (host_errno) {
189                 case TRY_AGAIN:
190                         snprintf(errmsg, errmsg_size, "Host name lookup failure");
191                         break;
192                 case NO_RECOVERY:
193                         snprintf(errmsg, errmsg_size, "Unknown server error");
194                         break;
195                 case NO_ADDRESS:
196                         snprintf(errmsg, errmsg_size, "No address associated with name");
197                         break;
198                 default:
199                         snprintf(errmsg, errmsg_size, "Unknown DNS error %d", host_errno);
200                 }
201                 return -1;
202         }
203
204         // The lookup succeeded, so we're good.
205         return 0;
206 }