diff -u src-old/auth.c src-patched/auth.c
--- src-old/auth.c	Fri Feb  2 10:57:34 2001
+++ src-patched/auth.c	Tue Jan 23 21:29:37 2001
@@ -958,14 +958,15 @@
 			return -1;
 		}
 
-		if (paircmp(authreq->request, authreq->check_pairs) != 0) {
+		if (paircmp_group(authreq->request, authreq->check_pairs, &authreq->reply_pairs) != 0) {
 			log(L_AUTH, "Check list does not match request list [%s] (%s)", namepair->strvalue, auth_name(authreq, 1));
 			rad_send_reply(PW_AUTHENTICATION_REJECT, authreq, NULL, NULL, activefd);
 			authfree(authreq);
 			return -1;
 		}
-			
-
+		
+    		fprint_attr_list(stdout,authreq->reply_pairs);
+	
 		/*
 		 *	Validate the user
 		 */
diff -u src-old/files.c src-patched/files.c
--- src-old/files.c	Fri Feb  2 10:57:34 2001
+++ src-patched/files.c	Thu Feb 15 17:54:24 2001
@@ -339,6 +339,7 @@
 	return retval;
 }
 
+
 /*
  *	Compare prefix/suffix.
  */
@@ -391,8 +392,6 @@
         return ret;
 }
 
-
-
 /*
  *	Compare two pair lists except for the password information.
  *	Return 0 on match.
@@ -549,6 +548,224 @@
 	return result;
 
 }
+
+/* Same as paircmp, but makes OR logic for groups having Fall-Through check item. *
+ * I.e., if any of check items for a group are not satisfied, group's reply 
+ * items are not added. However authentication does not fail.
+ * FIXME: assumes that all groups go in order in the VALUE_PAIR check.
+ */
+
+int paircmp_group(VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply_pairs) {
+	VALUE_PAIR *check_item = check;
+	VALUE_PAIR *auth_item;
+	char *cur_group = check->group_name;
+	int result = 0, old_result = -1;
+	char username[AUTH_STRING_LEN];
+	int compare;
+	int or_logic = 0;
+
+	while (check_item != NULL) {
+
+    		DEBUG2("cur_group: %s check_item_group: %s result: %i or_logic: %u", cur_group, check_item->group_name, result, or_logic);
+		if (strncmp(check_item->group_name, cur_group, sizeof(check_item->group_name)) != 0)
+		    { 
+		    /* OR check item logic checks */
+    		    if (strncmp(cur_group, "OR_", sizeof("OR_")-1) == 0) {
+			result = old_result;
+			}
+
+		    /* OR group selection logic checks */
+		    if (result != 0 && or_logic == 1)
+		        {
+			/* Delete failed group's reply items */
+			    DEBUG2("removing %s's reply items", cur_group);
+			    result = 0;
+    			    pairdelete_group(reply_pairs, cur_group);
+	                 }
+		    else if (result != 0) return -1; /* Fail if not an OR group */
+		    or_logic = 0; /* result OR logic indicator */
+		    cur_group = check_item->group_name; /* change current group name */
+		    old_result = -1;
+		    }
+
+		switch (check_item->attribute) {
+			/*
+			 *	Attributes we skip during comparison.
+			 *	These are "server" check items.
+			 */
+			case PW_FALL_THROUGH:
+			{
+			    or_logic = 1;
+			    check_item = check_item->next;
+			    continue;
+			}
+			case PW_EXPIRATION:
+			case PW_LOGIN_TIME:
+			case PW_PASSWORD:
+			case PW_CRYPT_PASSWORD:
+			case PW_AUTHTYPE:
+#ifdef PAM /* cjd 19980706 */
+                        case PAM_AUTH_ATTR:
+#endif
+			case PW_SIMULTANEOUS_USE:
+			case PW_MAX_HOURS:
+			case PW_MONTHLY_TIME_LIMIT:
+			case PW_TOTAL_TIME_LIMIT:
+			case PW_ACTIVATION:
+			case PW_RADIUS_OPERATOR:
+			case PW_STRIP_USERNAME:
+			case PW_DAILY_TIME_LIMIT:
+			case PW_WEEKLY_TIME_LIMIT:
+				check_item = check_item->next;
+				continue;
+		}
+		/*
+		 *	See if this item is present in the request.
+		 */
+
+		auth_item = request;
+		for (; auth_item != NULL; auth_item = auth_item->next) {
+			switch (check_item->attribute) {
+				case PW_PREFIX:
+				case PW_SUFFIX:
+				case PW_GROUP_NAME:
+				case PW_GROUP:
+					if (auth_item->attribute  !=
+					    PW_USER_NAME)
+						continue;
+					/* Sizes are the same */
+					strcpy(username, auth_item->strvalue);
+				case PW_HUNTGROUP_NAME:
+					break;
+				case PW_HINT:
+					if (auth_item->attribute !=
+					    check_item->attribute)
+						continue;
+					if (strcmp(check_item->strvalue,
+					    auth_item->strvalue) != 0)
+						continue;
+					break;
+				default:
+					if (auth_item->attribute !=
+					    check_item->attribute)
+						continue;
+			}
+			break;
+		}
+
+		if (auth_item == NULL) {
+			result = -1;
+			check_item = check_item->next;
+			continue;
+		}
+
+
+		/*
+		 *	OK it is present now compare them.
+		 */
+
+		compare = 0;	/* default result */
+		switch(check_item->type) {
+			case PW_TYPE_STRING:
+				if (check_item->attribute == PW_PREFIX ||
+				    check_item->attribute == PW_SUFFIX) {
+					if (presufcmp(check_item,
+					    auth_item->strvalue, username,
+					    sizeof(username)) != 0)
+						result = -1; break;
+				}
+				else
+				if (check_item->attribute == PW_GROUP_NAME ||
+				    check_item->attribute == PW_GROUP) {
+					compare = groupcmp(check_item, username);
+				}
+				else
+				if (check_item->attribute == PW_HUNTGROUP_NAME){
+                                        compare = (huntgroup_match(request,
+                                                check_item->strvalue) == 0);
+                                        DEBUG("COMPARE = %d", compare);                                               break;
+				}
+				else
+				compare = strcmp(auth_item->strvalue,
+						 check_item->strvalue);
+				break;
+
+			case PW_TYPE_INTEGER:
+				if (check_item->attribute == PW_NAS_PORT_ID) {
+					compare = portcmp(check_item,auth_item);
+					break;
+				}
+				/*FALLTHRU*/
+			case PW_TYPE_IPADDR:
+				compare = auth_item->lvalue - check_item->lvalue;
+				break;
+
+			default:
+				result = -1;
+				break;
+		}
+
+		switch (check_item->operator)
+		  {
+		  default:
+		  case PW_OPERATOR_EQUAL:
+		    if (compare != 0) result = -1;
+		    break;
+
+		  case PW_OPERATOR_NOT_EQUAL:
+		    if (compare == 0) result = -1;
+		    break;
+
+		  case PW_OPERATOR_LESS_THAN:
+		    if (compare >= 0) result = -1;
+		    break;
+
+		  case PW_OPERATOR_GREATER_THAN:
+		    if (compare <= 0) result = -1;
+		    break;
+		    
+		  case PW_OPERATOR_LESS_EQUAL:
+		    if (compare > 0) result = -1;
+		    break;
+
+		  case PW_OPERATOR_GREATER_EQUAL:
+		    if (compare < 0) result = -1;
+		    break;
+		  }
+
+		/* Groups with OR logic for check_items: at least one check item 
+		 * must be satisfied. */
+		if (strncmp(check_item->group_name, "OR_", sizeof("OR_")-1) == 0)
+		    {
+		    DEBUG2("OR group %s: old_result = %i", check_item->group_name, result);
+		    if (result == 0) old_result = 0;
+		    result = 0;
+		    }
+
+		check_item = check_item->next;
+
+	}
+
+	DEBUG2("cur_group: %s check_item_group: NULL result: %i or_logic: %u (last)", cur_group, result, or_logic);
+	/* can only happen for the last group in check item list */
+        /* OR check item logic checks */
+        if (strncmp(cur_group, "OR_", sizeof("OR_")-1) == 0) {
+		result = old_result;
+	}
+    
+        /* OR group selection logic checks */
+        if (result != 0 && or_logic == 1) 
+        {
+	/* Delete failed group's reply items */
+	    DEBUG2("removing %s's reply items", cur_group);
+	    result = 0;
+	    pairdelete_group(reply_pairs, cur_group);
+         }
+	    
+	return result;
+
+}
+
 
 /*
  *	Compare two pair lists. At least one of the check pairs
diff -u src-old/mysql.c src-patched/mysql.c
--- src-old/mysql.c	Fri Feb  2 10:57:34 2001
+++ src-patched/mysql.c	Tue Feb  6 10:06:04 2001
@@ -717,6 +717,7 @@
 	pair->type = attr->type;
 	pair->operator = PW_OPERATOR_EQUAL;
 	pair->source = mode;
+	strncpy(pair->group_name, row[1], sizeof(pair->group_name));
 	switch(pair->type) {
 
 #if defined( BINARY_FILTERS )
@@ -889,7 +890,8 @@
 			log(L_CONS|L_ERR, "out of memory");
 			return -1;
 		}
-		sprintf(querystr, "SELECT %s.id, %s.GroupName, %s.Attribute, %s.Value FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id", table, table, table, table, table, sql->config->sql_usergroup_table, authstr, sql->config->sql_usergroup_table, table, table);
+//		sprintf(querystr, "SELECT %s.id, %s.GroupName, %s.Attribute, %s.Value FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id", table, table, table, table, table, sql->config->sql_usergroup_table, authstr, sql->config->sql_usergroup_table, table, table);
+		sprintf(querystr, "SELECT %s.id, %s.GroupName, %s.Attribute, %s.Value FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY Groupname", table, table, table, table, table, sql->config->sql_usergroup_table, authstr, sql->config->sql_usergroup_table, table);
 
 	} else if (mode == PW_VP_REALMDATA) {
 		if ((querystr = malloc(strlen(username) + (strlen(table) * 7) + (strlen(sql->config->sql_realmgroup_table) * 3) + 125)) == NULL) {
diff -u src-old/radiusd.h src-patched/radiusd.h
--- src-old/radiusd.h	Fri Feb  2 10:57:34 2001
+++ src-patched/radiusd.h	Tue Jan 23 21:29:26 2001
@@ -69,6 +69,7 @@
 	int			source;
 	char			strvalue[AUTH_STRING_LEN];
 	struct value_pair	*next;
+	char			group_name[30];
 } VALUE_PAIR;
 
 typedef struct auth_req {
@@ -273,6 +274,7 @@
 struct passwd	*rad_getpwnam(char *);
 VALUE_PAIR	*pairfind(VALUE_PAIR *, int);
 void		pairdelete(VALUE_PAIR **, int);
+void		pairdelete_group(VALUE_PAIR **, char *group);
 void		pairadd(VALUE_PAIR **, VALUE_PAIR *);
 void		authfree(AUTH_REQ *authreq);
 #if defined (sun) && defined(__svr4__) || defined(__hpux__) || defined(aix)
@@ -303,6 +305,7 @@
 char		*auth_name(AUTH_REQ *authreq, int do_cid);
 int		read_config_files(SQLSOCK *socket);
 int		paircmp(VALUE_PAIR *request, VALUE_PAIR *check);
+int		paircmp_group(VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply_pairs);
 int		presufcmp(VALUE_PAIR *check, char *name, char *rest, int rl);
 void		pairmove(VALUE_PAIR **to, VALUE_PAIR **from);
 void		pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr);
Common subdirectories: src-old/sql_modules and src-patched/sql_modules
diff -u src-old/util.c src-patched/util.c
--- src-old/util.c	Fri Feb  2 10:57:34 2001
+++ src-patched/util.c	Mon Jan 29 11:36:16 2001
@@ -225,7 +225,7 @@
 
 
 /*
- *	Find the pair with the mathing attribute
+ *	Find the pair with the matching attribute
  */
 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
 {
@@ -236,7 +236,7 @@
 
 
 /*
- *	Delete the pair(s) with the mathing attribute
+ *	Delete the pair(s) with the matching attribute
  */
 void pairdelete(VALUE_PAIR **first, int attr)
 {
@@ -249,6 +249,27 @@
 				last->next = next;
 			else
 				*first = next;
+			free(i);
+		} else
+			last = i;
+	}
+}
+
+/*
+ *	Delete the pair(s) with the matching group
+ */
+void pairdelete_group(VALUE_PAIR **first, char *group)
+{
+	VALUE_PAIR *i, *next, *last = NULL;
+
+	for(i = *first; i; i = next) {
+		next = i->next;
+		if (strncmp(i->group_name, group, sizeof (i->group_name)-1) == 0) {
+			if (last)
+				last->next = next;
+			else {
+				*first = next;
+				}
 			free(i);
 		} else
 			last = i;
