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