]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/external/file_userip/ext_file_userip_acl.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / acl / external / file_userip / ext_file_userip_acl.cc
CommitLineData
ca02e0ec 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
ca02e0ec
AJ
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
6d7a7410
FC
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 */
f7f3304a 29#include "squid.h"
079b1d0f 30#include "helper/protocol_defines.h"
c152a447
AJ
31#include "rfc1738.h"
32#include "util.h"
33
074d6a40
AJ
34#include <cstdlib>
35#include <cstring>
c152a447
AJ
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
c152a447
AJ
49struct 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
56int match_user(char *, char *);
57int match_group(char *, char *);
58struct ip_user_dict *load_dict(FILE *);
59int dict_lookup(struct ip_user_dict *, char *, char *);
60
61/** Size of lines read from the dictionary file */
f53969cc 62#define DICT_BUFFER_SIZE 8196
c152a447
AJ
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.
3d9b21db 67 * If there?s no netmask (no /) in the in the lhs , a mask
c152a447
AJ
68 * 255.255.255.255 is assumed.
69 * It returns a pointer to the first entry of the linked list
70 */
71struct ip_user_dict *
68d57793 72load_dict(FILE * FH) {
f53969cc
SM
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 */
c152a447 77 char line[DICT_BUFFER_SIZE]; /* the buffer for the lines read
f53969cc
SM
78 from the dict file */
79 char *tmpbuf; /* for the address before the
80 bitwise AND */
c152a447
AJ
81
82 /* the pointer to the first entry in the linked list */
c14fb378 83 first_entry = static_cast<struct ip_user_dict*>(xmalloc(sizeof(struct ip_user_dict)));
c152a447
AJ
84 current_entry = first_entry;
85
ed7f0868
AJ
86 unsigned int lineCount = 0;
87 while (fgets(line, sizeof(line), FH) != NULL) {
88 ++lineCount;
c152a447
AJ
89 if (line[0] == '#') {
90 continue;
91 }
ed7f0868
AJ
92
93 char *cp; // a char pointer used to parse each line.
c152a447
AJ
94 if ((cp = strchr (line, '\n')) != NULL) {
95 /* chop \n characters */
96 *cp = '\0';
97 }
ed7f0868
AJ
98 if (strtok(line, "\t ") != NULL) {
99 // NP: line begins with IP/mask. Skipped to the end of it with this strtok()
100
c152a447 101 /* get the username */
ed7f0868
AJ
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
c152a447
AJ
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 {
2f8abb64 119 /* when there's no slash, we figure the netmask is /32 */
c152a447
AJ
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 =
c14fb378 131 static_cast<struct ip_user_dict*>(xmalloc(sizeof(struct ip_user_dict)));
c152a447
AJ
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 */
145int
146dict_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",
68d57793
A
154 current_entry->username, current_entry->address,
155 current_entry->netmask);
c152a447
AJ
156
157 if ((inet_addr (address) & (unsigned long) current_entry->
158 netmask) == current_entry->address) {
3d9b21db 159 /* If the username contains an @ we assume it?s a group and
c152a447
AJ
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
176int
177match_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;
f53969cc 187} /* match_user */
c152a447
AJ
188
189int
190match_group(char *dict_group, char *username)
191{
f53969cc
SM
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 */
c152a447
AJ
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
211static void
212usage(const char *program_name)
213{
214 fprintf (stderr, "Usage:\n%s [-d] -f <configuration file>\n",
215 program_name);
216}
217
218int
219main (int argc, char *argv[])
220{
c152a447
AJ
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);
24885773 240 exit(EXIT_SUCCESS);
c152a447
AJ
241 default:
242 fprintf(stderr, "%s: FATAL: Unknown parameter option '%c'", program_name, ch);
243 usage(program_name);
24885773 244 exit(EXIT_FAILURE);
c152a447
AJ
245 }
246 }
247 if (filename == NULL) {
248 fprintf(stderr, "%s: FATAL: No Filename configured.", program_name);
249 usage(program_name);
24885773 250 exit(EXIT_FAILURE);
c152a447 251 }
ed7f0868
AJ
252 FILE *FH = fopen(filename, "r");
253 if (!FH) {
b69e9ffa
AJ
254 int xerrno = errno;
255 fprintf(stderr, "%s: FATAL: Unable to open file '%s': %s", program_name, filename, xstrerr(xerrno));
24885773 256 exit(EXIT_FAILURE);
ed7f0868 257 }
c152a447
AJ
258 current_entry = load_dict(FH);
259
260 while (fgets(line, HELPER_INPUT_BUFFER, stdin)) {
261 if ((cp = strchr (line, '\n')) == NULL) {
262 /* too large message received.. skip and deny */
263 fprintf(stderr, "%s: ERROR: Input Too Large: %s\n", program_name, line);
264 while (fgets(line, sizeof(line), stdin)) {
265 fprintf(stderr, "%s: ERROR: Input Too Large..: %s\n", program_name, line);
266 if (strchr(line, '\n') != NULL)
267 break;
268 }
194ccc9c 269 SEND_BH(HLP_MSG("Input Too Large."));
c152a447
AJ
270 continue;
271 }
272 *cp = '\0';
273 address = strtok(line, " \t");
274 username = strtok(NULL, " \t");
275 if (!address || !username) {
276 debug("%s: unable to read tokens\n", program_name);
194ccc9c 277 SEND_BH(HLP_MSG("Invalid Input."));
c152a447
AJ
278 continue;
279 }
280 rfc1738_unescape(address);
281 rfc1738_unescape(username);
282 int result = dict_lookup(current_entry, username, address);
283 debug("%s: result: %d\n", program_name, result);
284 if (result != 0) {
285 SEND_OK("");
286 } else {
287 SEND_ERR("");
288 }
289 }
290
291 fclose (FH);
24885773 292 return EXIT_SUCCESS;
c152a447 293}
f53969cc 294