]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/external_acl/file_userip/ext_file_userip_acl.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / external_acl / file_userip / ext_file_userip_acl.cc
1 /*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /*
10 * Copyright (C) 2002 Rodrigo Campos
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 * Author: Rodrigo Campos (rodrigo@geekbunker.org)
27 *
28 */
29 #include "squid.h"
30 #include "helpers/defines.h"
31 #include "rfc1738.h"
32 #include "util.h"
33
34 #include <cstdlib>
35 #include <cstring>
36 #if HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #if HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #if HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #if HAVE_GRP_H
46 #include <grp.h>
47 #endif
48
49 struct ip_user_dict {
50 unsigned long address; // IP address (assumes IPv4)
51 unsigned long netmask; // IP netmask
52 char *username;
53 struct ip_user_dict *next_entry;
54 };
55
56 int match_user(char *, char *);
57 int match_group(char *, char *);
58 struct ip_user_dict *load_dict(FILE *);
59 int dict_lookup(struct ip_user_dict *, char *, char *);
60
61 /** Size of lines read from the dictionary file */
62 #define DICT_BUFFER_SIZE 8196
63
64 /** This function parses the dictionary file and loads it
65 * in memory. All IP addresses are processed with a bitwise AND
66 * with their netmasks before they are stored.
67 * If there?s no netmask (no /) in the in the lhs , a mask
68 * 255.255.255.255 is assumed.
69 * It returns a pointer to the first entry of the linked list
70 */
71 struct ip_user_dict *
72 load_dict(FILE * FH) {
73 struct ip_user_dict *current_entry; /* the structure used to
74 store data */
75 struct ip_user_dict *first_entry = NULL; /* the head of the
76 linked list */
77 char line[DICT_BUFFER_SIZE]; /* the buffer for the lines read
78 from the dict file */
79 char *tmpbuf; /* for the address before the
80 bitwise AND */
81
82 /* the pointer to the first entry in the linked list */
83 first_entry = (struct ip_user_dict*)malloc(sizeof(struct ip_user_dict));
84 current_entry = first_entry;
85
86 unsigned int lineCount = 0;
87 while (fgets(line, sizeof(line), FH) != NULL) {
88 ++lineCount;
89 if (line[0] == '#') {
90 continue;
91 }
92
93 char *cp; // a char pointer used to parse each line.
94 if ((cp = strchr (line, '\n')) != NULL) {
95 /* chop \n characters */
96 *cp = '\0';
97 }
98 if (strtok(line, "\t ") != NULL) {
99 // NP: line begins with IP/mask. Skipped to the end of it with this strtok()
100
101 /* get the username */
102 char *username;
103 if ((username = strtok(NULL, "\t ")) == NULL) {
104 debug("Missing username on line %u of dictionary file\n", lineCount);
105 continue;
106 }
107
108 /* look for a netmask */
109 if ((cp = strtok (line, "/")) != NULL) {
110 /* store the ip address in a temporary buffer */
111 tmpbuf = cp;
112 cp = strtok (NULL, "/");
113 if (cp != NULL) {
114 /* if we have a slash in the lhs, we have a netmask */
115 current_entry->netmask = (inet_addr(cp));
116 current_entry->address =
117 (((inet_addr (tmpbuf))) & current_entry->netmask);
118 } else {
119 /* when theres no slash, we figure the netmask is /32 */
120 current_entry->address = (inet_addr(tmpbuf));
121 current_entry->netmask = (inet_addr("255.255.255.255"));
122 }
123 }
124 /* get space for the username */
125 current_entry->username =
126 (char*)calloc(strlen(username) + 1, sizeof(char));
127 strcpy(current_entry->username, username);
128
129 /* get space and point current_entry to the new entry */
130 current_entry->next_entry =
131 (struct ip_user_dict*)malloc(sizeof(struct ip_user_dict));
132 current_entry = current_entry->next_entry;
133 }
134
135 }
136
137 /* Return a pointer to the first entry linked list */
138 return first_entry;
139 }
140
141 /** This function looks for a matching ip/mask in
142 * the dict file loaded in memory.
143 * It returns 1 if it finds a match or 0 if no match is found
144 */
145 int
146 dict_lookup(struct ip_user_dict *first_entry, char *username,
147 char *address)
148 {
149 /* Move the pointer to the first entry of the linked list. */
150 struct ip_user_dict *current_entry = first_entry;
151
152 while (current_entry->username != NULL) {
153 debug("user: %s\naddr: %lu\nmask: %lu\n\n",
154 current_entry->username, current_entry->address,
155 current_entry->netmask);
156
157 if ((inet_addr (address) & (unsigned long) current_entry->
158 netmask) == current_entry->address) {
159 /* If the username contains an @ we assume it?s a group and
160 call the corresponding function */
161 if ((strchr (current_entry->username, '@')) == NULL) {
162 if ((match_user (current_entry->username, username)) == 1)
163 return 1;
164 } else {
165 if ((match_group (current_entry->username, username)) == 1)
166 return 1;
167 }
168 }
169 current_entry = current_entry->next_entry;
170 }
171
172 /* If no match was found we return 0 */
173 return 0;
174 }
175
176 int
177 match_user(char *dict_username, char *username)
178 {
179 if ((strcmp(dict_username, username)) == 0) {
180 return 1;
181 } else {
182 if ((strcmp(dict_username, "ALL")) == 0) {
183 return 1;
184 }
185 }
186 return 0;
187 } /* match_user */
188
189 int
190 match_group(char *dict_group, char *username)
191 {
192 struct group *g; /* a struct to hold group entries */
193 ++dict_group; /* the @ should be the first char
194 so we rip it off by incrementing
195 * the pointer by one */
196
197 if ((g = getgrnam(dict_group)) == NULL) {
198 debug("Group does not exist '%s'\n", dict_group);
199 return 0;
200 } else {
201 while (*(g->gr_mem) != NULL) {
202 if (strcmp(*((g->gr_mem)++), username) == 0) {
203 return 1;
204 }
205 }
206 }
207 return 0;
208
209 }
210
211 static void
212 usage(const char *program_name)
213 {
214 fprintf (stderr, "Usage:\n%s [-d] -f <configuration file>\n",
215 program_name);
216 }
217
218 int
219 main (int argc, char *argv[])
220 {
221 char *filename = NULL;
222 char *program_name = argv[0];
223 char *cp;
224 char *username, *address;
225 char line[HELPER_INPUT_BUFFER];
226 struct ip_user_dict *current_entry;
227 int ch;
228
229 setvbuf (stdout, NULL, _IOLBF, 0);
230 while ((ch = getopt(argc, argv, "df:h")) != -1) {
231 switch (ch) {
232 case 'f':
233 filename = optarg;
234 break;
235 case 'd':
236 debug_enabled = 1;
237 break;
238 case 'h':
239 usage(program_name);
240 exit (0);
241 default:
242 fprintf(stderr, "%s: FATAL: Unknown parameter option '%c'", program_name, ch);
243 usage(program_name);
244 exit (1);
245 }
246 }
247 if (filename == NULL) {
248 fprintf(stderr, "%s: FATAL: No Filename configured.", program_name);
249 usage(program_name);
250 exit(1);
251 }
252 FILE *FH = fopen(filename, "r");
253 if (!FH) {
254 fprintf(stderr, "%s: FATAL: Unable to open file '%s': %s", program_name, filename, xstrerror());
255 exit(1);
256 }
257 current_entry = load_dict(FH);
258
259 while (fgets(line, HELPER_INPUT_BUFFER, stdin)) {
260 if ((cp = strchr (line, '\n')) == NULL) {
261 /* too large message received.. skip and deny */
262 fprintf(stderr, "%s: ERROR: Input Too Large: %s\n", program_name, line);
263 while (fgets(line, sizeof(line), stdin)) {
264 fprintf(stderr, "%s: ERROR: Input Too Large..: %s\n", program_name, line);
265 if (strchr(line, '\n') != NULL)
266 break;
267 }
268 SEND_ERR("Input Too Large.");
269 continue;
270 }
271 *cp = '\0';
272 address = strtok(line, " \t");
273 username = strtok(NULL, " \t");
274 if (!address || !username) {
275 debug("%s: unable to read tokens\n", program_name);
276 SEND_ERR("Invalid Input.");
277 continue;
278 }
279 rfc1738_unescape(address);
280 rfc1738_unescape(username);
281 int result = dict_lookup(current_entry, username, address);
282 debug("%s: result: %d\n", program_name, result);
283 if (result != 0) {
284 SEND_OK("");
285 } else {
286 SEND_ERR("");
287 }
288 }
289
290 fclose (FH);
291 return 0;
292 }
293