--- /dev/null
+$Id: README,v 1.1 2002/06/23 14:25:16 hno Exp $
+
+README file for ip_user_check, an external helper for the
+Squid external acl scheme.
+
+It works by reading a pair composed by an ip address and an username
+on STDIN and matching it against a configuration file.
+
+The configuration for the external ACL should be:
+
+external_acl_type type-name %SRC %LOGIN /path/to/ip_user_check -f /path/to/config.file
+
+If the program finds a matching username/ip in the configuration file,
+it returns `OK', or `ERR' otherwise.
+
+The usage for the program is:
+
+ip_user_check -f <configuration_file>
+
+
+The configuration file format is as follows:
+
+ip_addr[/mask] user|@group|ALL|NONE
+
+Where ip_addr is a dotted quad format IP address, the mask
+must be in dotted quad format too.
+
+When the second parameter is prefixed with an @, the program will lookup in the
+/etc/group entry for the specified username.
+
+There are other two directives, `ALL' and `NONE', which mean "any user on this ip
+address may authenticate" or "no user on this ip address may authenticate".
+
+TODO
+- Deny operator, to create `allow all but' rules
+- Check for a valid user in the OS
+- Accept decimal format netmasks
+
+
+--
+Rodrigo Campos
+rodrigo@geekbunker.org
--- /dev/null
+/* $Id: dict.c,v 1.1 2002/06/23 14:25:17 hno Exp $
+* Copyright (C) 2002 Rodrigo Campos
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Author: Rodrigo Campos (rodrigo@geekbunker.org)
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "ip_user.h"
+
+#ifndef DEBUG
+#undef DEBUG
+#endif
+
+
+/* This function parses the dictionary file and loads it
+ * in memory. All IP addresses are processed with a bitwise AND
+ * with their netmasks before they are stored.
+ * If there´s no netmask (no /) in the in the lhs , a mask
+ * 255.255.255.255 is assumed.
+ * It returns a pointer to the first entry of the linked list
+ */
+struct ip_user_dict *
+load_dict (FILE * FH)
+{
+ struct ip_user_dict *current_entry; /* the structure used to
+ store data */
+ struct ip_user_dict *first_entry = NULL; /* the head of the
+ linked list */
+ char line[BUFSIZE]; /* the buffer for the lines read
+ from the dict file */
+ char *cp; /* a char pointer used to parse
+ each line */
+ char *username; /* for the username */
+ char *tmpbuf; /* for the address before the
+ bitwise AND */
+
+ /* the pointer to the first entry in the linked list */
+ first_entry = malloc (sizeof (struct ip_user_dict));
+ current_entry = first_entry;
+
+ while ((cp = fgets (line, sizeof (line), FH)) != NULL) {
+ if (line[0] == '#') {
+ continue;
+ }
+ if ((cp = strchr (line, '\n')) != NULL) {
+ /* chop \n characters */
+ *cp = '\0';
+ }
+ if ((cp = strtok (line, "\t ")) != NULL) {
+ /* get the username */
+ username = strtok (NULL, "\t ");
+ /* look for a netmask */
+ if ((cp = strtok (line, "/")) != NULL) {
+ /* store the ip address in a temporary buffer */
+ tmpbuf = cp;
+ cp = strtok (NULL, "/");
+ if (cp != NULL) {
+ /* if we have a slash in the lhs, we have a netmask */
+ current_entry->netmask = (inet_addr (cp));
+ current_entry->address =
+ (((inet_addr (tmpbuf))) & current_entry->netmask);
+ } else {
+ /* when theres no slash, we figure the netmask is /32 */
+ current_entry->address = (inet_addr (tmpbuf));
+ current_entry->netmask = (inet_addr ("255.255.255.255"));
+ }
+ }
+ /* get space for the username */
+ current_entry->username =
+ calloc (strlen (username) + 1, sizeof (char));
+ strcpy (current_entry->username, username);
+
+ /* get space and point current_entry to the new entry */
+ current_entry->next_entry =
+ malloc (sizeof (struct ip_user_dict));
+ current_entry = current_entry->next_entry;
+ }
+
+ }
+
+ /* Return a pointer to the first entry linked list */
+ return first_entry;
+} /* load_dict */
+
+/* This function looks for a matching ip/mask in
+ * the dict file loaded in memory.
+ * It returns 1 if it finds a match or 0 if no match is found
+ */
+int
+dict_lookup (struct ip_user_dict *first_entry, char *username,
+ char *address)
+{
+ /* Move the pointer to the first entry of the linked list. */
+ struct ip_user_dict *current_entry = first_entry;
+
+ while (current_entry->username != NULL) {
+#ifdef DEBUG
+ printf ("user: %s\naddr: %lu\nmask: %lu\n\n",
+ current_entry->username, current_entry->address,
+ current_entry->netmask);
+#endif
+
+ if ((inet_addr (address) & (unsigned long) current_entry->
+ netmask) == current_entry->address) {
+ /* If the username contains an @ we assume it´s a group and
+ call the corresponding function */
+ if ((strchr (current_entry->username, '@')) == NULL) {
+ if ((match_user (current_entry->username, username)) == 1)
+ return 1;
+ } else {
+ if ((match_group (current_entry->username, username)) == 1)
+ return 1;
+ }
+ }
+ current_entry = current_entry->next_entry;
+ }
+
+ /* If no match was found we return 0 */
+ return 0;
+} /* dict_lookup */
--- /dev/null
+192.168.1.0/255.255.255.0 diniz
+0.0.0.0/0.0.0.0 NONE
--- /dev/null
+# $Id: example.conf,v 1.1 2002/06/23 14:25:17 hno Exp $
+# Lines that begin with a # are ignored
+# The main format is:
+#
+# Single user
+# ip[/mask] user
+#
+# Users that belong to `group´ (/etc/group)
+# ip[/mask] @group
+#
+# No User from this IP
+# ip[/mask] NONE
+#
+# All Users from this IP
+# ip[/mask] ALL
+#
+# IP and MASK must be in dotted quad format.
+#
+# Ths first match wins, so you may create rules that
+# `allow everyone but foo bar´ or
+# `deny everyone but foo bar´
+#
+# Examples:
+# All users from the 192.168.1.0/24 network are allowed
+# 192.168.1.0/255.255.255.0 ALL
+#
+# Users from the 192.168.2.0/24 network are not allowed
+# except for user `boss´ that can authenticate from
+# anywhere
+# 0.0.0.0/0.0.0.0 boss
+# 192.168.2.0/255.255.255.0 NONE
+#
+# User `jayk´ may athenticate only from his station
+# ip address
+# 192.168.3.45 jayk
+#
+# Users of the `tol´ group may authenticate from
+# their VLAN
+# 10.0.0.0/255.255.0.0 @tol
--- /dev/null
+/* $Id: ip_user.h,v 1.1 2002/06/23 14:25:17 hno Exp $
+* Copyright (C) 2002 Rodrigo Campos
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Author: Rodrigo Campos (rodrigo@geekbunker.org)
+*
+*/
+
+
+
+
+struct ip_user_dict
+{
+ unsigned long address;
+ unsigned long netmask;
+ char *username;
+ struct ip_user_dict *next_entry;
+};
+
+extern int match_user(char *, char *);
+extern int match_group(char *, char *);
+extern struct ip_user_dict *load_dict(FILE *);
+extern int dict_lookup(struct ip_user_dict *, char *, char *);
+
+
+#define BUFSIZE 1024
--- /dev/null
+* Copyright (C) 2002 Rodrigo Campos
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Author: Rodrigo Campos (rodrigo@geekbunker.org)
+*
+*/
--- /dev/null
+/* $Id: main.c,v 1.1 2002/06/23 14:25:17 hno Exp $
+* Copyright (C) 2002 Rodrigo Campos
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Author: Rodrigo Campos (rodrigo@geekbunker.org)
+*
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+
+#include "ip_user.h"
+
+
+void
+usage (char *program_name)
+{
+ fprintf (stderr, "Usage:\n%s -f <configuration file>\n",
+ program_name);
+}
+
+int
+main (int argc, char *argv[])
+{
+ FILE *FH;
+ char *filename = NULL;
+ char *program_name = argv[0];
+ char *cp;
+ char *username, *address;
+ char line[BUFSIZE];
+ struct ip_user_dict *current_entry;
+ int ch;
+
+ setvbuf (stdout, NULL, _IOLBF, 0);
+ while ((ch = getopt (argc, argv, "f:")) != -1) {
+ switch (ch) {
+ case 'f':
+ filename = optarg;
+ break;
+ default:
+ usage (program_name);
+ exit (1);
+ }
+ }
+ if (filename == NULL) {
+ usage (program_name);
+ exit(1);
+ }
+ FH = fopen (filename, "r");
+ current_entry = load_dict (FH);
+
+ while (fgets (line, sizeof (line), stdin)) {
+ if ((cp = strchr (line, '\n')) != NULL) {
+ *cp = '\0';
+ }
+ if ((cp = strtok (line, " \t")) != NULL) {
+ address = cp;
+ username = strtok (NULL, " \t");
+ } else {
+ fprintf (stderr, "helper: unable to read tokens\n");
+ }
+#ifdef DEBUG
+ printf ("result: %d\n",
+ dict_lookup (current_entry, username, address));
+#endif
+ if ((dict_lookup (current_entry, username, address)) != 0) {
+ printf ("OK\n");
+ } else {
+ printf ("ERR\n");
+ }
+
+
+ }
+
+
+ fclose (FH);
+ return 0;
+}
--- /dev/null
+/* $Id: match.c,v 1.1 2002/06/23 14:25:17 hno Exp $
+* Copyright (C) 2002 Rodrigo Campos
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Author: Rodrigo Campos (rodrigo@geekbunker.org)
+*
+*/
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <grp.h>
+
+
+int
+match_user (char *dict_username, char *username)
+{
+ if ((strcmp (dict_username, username)) == 0) {
+ return 1;
+ } else {
+ if ((strcmp (dict_username, "ALL")) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+} /* match_user */
+
+int
+match_group (char *dict_group, char *username)
+{
+ struct group *g; /* a struct to hold group entries */
+ dict_group++; /* the @ should be the first char
+ so we rip it off by incrementing
+ * the pointer by one */
+
+ if ((g = getgrnam (dict_group)) == NULL) {
+ fprintf (stderr, "helper: Group does not exist '%s'\n",
+ dict_group);
+ return 0;
+ } else {
+ while (*(g->gr_mem) != NULL) {
+ if (strcmp (*((g->gr_mem)++), username) == 0) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+
+} /* match_group */