]>
Commit | Line | Data |
---|---|---|
ca02e0ec AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 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 | ||
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" |
c152a447 AJ |
30 | #include "helpers/defines.h" |
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 |
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. | |
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 | */ | |
71 | struct ip_user_dict * | |
68d57793 | 72 | load_dict(FILE * FH) { |
c152a447 AJ |
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 */ | |
c152a447 AJ |
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 | ||
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 { | |
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", | |
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 | ||
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 */ | |
755494da | 193 | ++dict_group; /* the @ should be the first char |
c152a447 AJ |
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 | { | |
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); | |
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 | } | |
ed7f0868 AJ |
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 | } | |
c152a447 AJ |
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 | } |