From pdp@nl.demon.net  Sun Feb 15 12:11:44 2004
Return-Path: <pdp@nl.demon.net>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id E4E3016A4CE
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 15 Feb 2004 12:11:43 -0800 (PST)
Received: from hermes.mail.nl.demon.net (hermes.mail.nl.demon.net [194.159.72.197])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 6BCFF43D1D
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 15 Feb 2004 12:11:43 -0800 (PST)
	(envelope-from pdp@nl.demon.net)
Received: from scimitar.noc.nl.demon.net ([194.159.72.251])
	by hermes.mail.nl.demon.net with esmtp (Exim 3.36 #2)
	id 1AsScA-000Nl4-00
	for FreeBSD-gnats-submit@freebsd.org; Sun, 15 Feb 2004 20:11:42 +0000
Received: by scimitar.noc.nl.demon.net with local
	id 1AsScA-0007BP-Dv; Sun, 15 Feb 2004 20:11:42 +0000
Message-Id: <E1AsScA-0007BP-Dv@scimitar.noc.nl.demon.net>
Date: Sun, 15 Feb 2004 20:11:42 +0000
From: Phil Pennock <pdp+freebsd@nl.demon.net>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: pam_radius doesn't maintain multiple state fields
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         62885
>Category:       bin
>Synopsis:       pam_radius(8) doesn't maintain multiple state fields
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    des
>State:          feedback
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 15 12:20:05 PST 2004
>Closed-Date:    
>Last-Modified:  Tue Feb  1 02:00:20 UTC 2011
>Originator:     Phil Pennock
>Release:        FreeBSD 4.7-RELEASE-p25 and 5.2-RELEASE-p2 i386
>Organization:
Demon Internet Netherlands
>Environment:
FreeBSD/x86 both 4.x and 5.x
Problem present in, and patch for:
  src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.2.6.1
Problem still present in:
  src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.19
>Description:
RADIUS packets can include multiple state fields, which are an
order-dependent collection of state information.  All state fields
should be maintained.  The current pam_radius implementation supports
just one state field.  This is a bug.

There's also no support for an initial state field or for a usercode
suffix; the patch below fixes this too, as a feature-request level
enhancement rather than a bug-fix.
>How-To-Repeat:
Use an authentication system which uses challenge/response over RADIUS
with multiple state fields.  Probably difficult to find if you don't
have such an environment set up already.
>Fix:
This patch supports:
 * up to 10 state fields which need to be maintained
 * an initial state field which should be set with the first packet
 * a usercode suffix to be added to the user string
   (eg. "@domain" or ".domain.suffix")

This patch adds one one-shot memory-leak, where the usercode has the
user_suffix added.  I'm not familiar enough with the PAM programming
environment to know what is a safe, reliable and correct fix for this.
Sorry.

--- pam_radius.c.pre-pdp	Sun Feb 15 19:19:38 2004
+++ pam_radius.c	Sun Feb 15 20:50:35 2004
@@ -53,20 +53,30 @@
 
 enum {
 	PAM_OPT_CONF = PAM_OPT_STD_MAX,
-	PAM_OPT_TEMPLATE_USER
+	PAM_OPT_TEMPLATE_USER,
+	PAM_OPT_USER_SUFFIX,
+	PAM_OPT_INITIAL_STATE,
 };
 
 static struct opttab other_options[] = {
 	{ "conf",		PAM_OPT_CONF },
 	{ "template_user",	PAM_OPT_TEMPLATE_USER },
+	{ "user_suffix",	PAM_OPT_USER_SUFFIX },
+	{ "initial_state",	PAM_OPT_INITIAL_STATE },
 	{ NULL, 0 }
 };
 
+struct state_items {
+	void 	*item;
+	size_t	 len;
+};
+
+#define MAX_STATE_ITEMS		10
 #define	MAX_CHALLENGE_MSGS	10
 #define	PASSWORD_PROMPT		"RADIUS Password:"
 
 static int	 build_access_request(struct rad_handle *, const char *,
-		    const char *, const void *, size_t);
+		    const char *, const struct state_items *);
 static int	 do_accept(pam_handle_t *, struct rad_handle *);
 static int	 do_challenge(pam_handle_t *, struct rad_handle *,
 		    const char *);
@@ -77,9 +87,10 @@
  */
 static int
 build_access_request(struct rad_handle *radh, const char *user,
-    const char *pass, const void *state, size_t state_len)
+    const char *pass, const struct state_items *states)
 {
 	char	 host[MAXHOSTNAMELEN];
+	int	 state_index = 0;
 
 	if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) {
 		syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh));
@@ -94,10 +105,17 @@
 		syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh));
 		return (-1);
 	}
-	if (state != NULL && rad_put_attr(radh, RAD_STATE, state,
-	    state_len) == -1) {
-		syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh));
-		return (-1);
+	if (states != NULL) {
+		while (states[state_index].item != NULL) {
+			if (rad_put_attr(radh, RAD_STATE,
+				    states[state_index].item,
+				    states[state_index].len) == -1) {
+				syslog(LOG_CRIT, "rad_put_attr: %s",
+				    rad_strerror(radh));
+				return (-1);
+			}
+			++state_index;
+		}
 	}
 	if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) {
 		syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh));
@@ -115,7 +133,8 @@
 	char *s;
 
 	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
-		if (attrtype == RAD_USER_NAME) {
+		switch (attrtype) {
+		case RAD_USER_NAME:
 			s = rad_cvt_string(attrval, attrlen);
 			if (s == NULL) {
 				syslog(LOG_CRIT,
@@ -124,6 +143,7 @@
 			}
 			pam_set_item(pamh, PAM_USER, s);
 			free(s);
+			break;
 		}
 	}
 	if (attrtype == -1) {
@@ -140,8 +160,8 @@
 	int attrtype;
 	const void *attrval;
 	size_t attrlen;
-	const void *state;
-	size_t statelen;
+	struct state_items states[MAX_STATE_ITEMS+1];
+	int num_states;
 	struct pam_message msgs[MAX_CHALLENGE_MSGS];
 	const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS];
 	struct pam_response *resp;
@@ -149,15 +169,21 @@
 	const void *item;
 	const struct pam_conv *conv;
 
-	state = NULL;
-	statelen = 0;
+	memset(states, 0, sizeof(states));
+	num_states = 0;
 	num_msgs = 0;
 	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
 		switch (attrtype) {
 
 		case RAD_STATE:
-			state = attrval;
-			statelen = attrlen;
+			if (num_states >= MAX_STATE_ITEMS) {
+				syslog(LOG_ERR,
+				    "Too many RADIUS state fields in response");
+				return (PAM_SERVICE_ERR);
+			}
+			states[num_states].item = (void *)attrval;
+			states[num_states].len = (size_t)attrlen;
+			++num_states;
 			break;
 
 		case RAD_REPLY_MESSAGE:
@@ -201,8 +227,7 @@
 	if ((retval = conv->conv(num_msgs, msg_ptrs, &resp,
 	    conv->appdata_ptr)) != PAM_SUCCESS)
 		return (retval);
-	if (build_access_request(radh, user, resp[num_msgs-1].resp, state,
-	    statelen) == -1)
+	if (build_access_request(radh, user, resp[num_msgs-1].resp, states) == -1)
 		return (PAM_SERVICE_ERR);
 	memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp));
 	free(resp[num_msgs-1].resp);
@@ -218,8 +243,9 @@
 {
 	struct options options;
 	struct rad_handle *radh;
+	struct state_items *states;
 	const char *user, *tmpuser, *pass;
-	char *conf_file, *template_user;
+	char *conf_file, *template_user, *user_suffix, *initial_state;
 	int retval;
 	int e;
 
@@ -231,6 +257,20 @@
 	pam_test_option(&options, PAM_OPT_CONF, &conf_file);
 	template_user = NULL;
 	pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user);
+	user_suffix = NULL;
+	pam_test_option(&options, PAM_OPT_USER_SUFFIX, &user_suffix);
+	initial_state = NULL;
+	pam_test_option(&options, PAM_OPT_INITIAL_STATE, &initial_state);
+
+	if (initial_state) {
+		states = calloc(2, sizeof(struct state_items));
+		states[0].item = initial_state;
+		states[0].len = strlen(initial_state);
+		states[1].item = NULL;
+		states[1].len = 0;
+	} else {
+		states = NULL;
+	}
 
 	retval = pam_get_user(pamh, &user, NULL);
 	if (retval != PAM_SUCCESS)
@@ -238,6 +278,21 @@
 
 	PAM_LOG("Got user: %s", user);
 
+	/* slight memory leak; one-shot, minimal and tolerable */
+	if (user_suffix && strlen(user_suffix)) {
+		size_t sz;
+		char *p;
+
+		sz = strlen(user) + strlen(user_suffix) + 1;
+		p = malloc(sz);
+		if (!p) {
+			syslog(LOG_CRIT, "malloc failed: %m");
+			return (PAM_SERVICE_ERR);
+		}
+		sprintf(p, "%s%s", user, user_suffix);
+		user = p;
+	}
+
 	retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
 	if (retval != PAM_SUCCESS)
 		return (retval);
@@ -260,7 +315,7 @@
 
 	PAM_LOG("Radius config file read");
 
-	if (build_access_request(radh, user, pass, NULL, 0) == -1) {
+	if (build_access_request(radh, user, pass, states) == -1) {
 		rad_close(radh);
 		return (PAM_SERVICE_ERR);
 	}
@@ -305,6 +360,7 @@
 			return (PAM_AUTH_ERR);
 
 		case RAD_ACCESS_CHALLENGE:
+			PAM_LOG("Have RAD_ACCESS_CHALLENGE packet");
 			retval = do_challenge(pamh, radh, user);
 			if (retval != PAM_SUCCESS) {
 				rad_close(radh);
>Release-Note:
>Audit-Trail:

From: Phil Pennock <pdp@nl.demon.net>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/62885: pam_radius doesn't maintain multiple state fields
Date: Mon, 16 Feb 2004 19:06:51 +0100

 On 2004-02-15 at 12:20 -0800, FreeBSD-gnats-submit@FreeBSD.org wrote:
 > http://www.freebsd.org/cgi/query-pr.cgi?pr=62885
 > 
 > >Category:       bin
 > >Responsible:    freebsd-bugs
 > >Synopsis:       pam_radius doesn't maintain multiple state fields
 > >Arrival-Date:   Sun Feb 15 12:20:05 PST 2004
 
 On a system running 4.8-RELEASE-p3, I encountered the PAM_OPT_NAS_ID
 support, which breaks the patch submitted.
 
 On that system, I also used the new pam_radius for "sshd", not "sudo".
 In so doing, I found that whilst the basic fixed functionality was
 correct, the added feature of "user_suffix" did not work when the same
 user was being used as an account -- the suffix stayed around.
 
 This revised patch is against:
  src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.2.6.2
 
 This patch also adds the "challenge_only" option which, if set, omits
 the initial password prompt (which would need to supply an empty
 password anyway).  Because libradius refuses to send an auth packet with
 no password (correct RFC behaviour there) I supply an empty string in
 this case.
 
 Perhaps the "user_suffix" option should be replaced with
 "domain_separator" and "default_domain" instead; if desired, I can
 rework to that slightly more generic interface.
 
 Thanks,
 
 -----------------------------< cut here >-------------------------------
 --- pam_radius.c.orig	Tue Apr  8 13:20:23 2003
 +++ pam_radius.c	Mon Feb 16 17:53:12 2004
 @@ -54,22 +54,34 @@
  enum {
  	PAM_OPT_CONF = PAM_OPT_STD_MAX,
  	PAM_OPT_TEMPLATE_USER,
 -	PAM_OPT_NAS_ID
 +	PAM_OPT_NAS_ID,
 +	PAM_OPT_USER_SUFFIX,
 +	PAM_OPT_INITIAL_STATE,
 +	PAM_OPT_CHALLENGE_ONLY
  };
  
  static struct opttab other_options[] = {
  	{ "conf",		PAM_OPT_CONF },
  	{ "template_user",	PAM_OPT_TEMPLATE_USER },
  	{ "nas_id",		PAM_OPT_NAS_ID },
 +	{ "user_suffix",	PAM_OPT_USER_SUFFIX },
 +	{ "initial_state",	PAM_OPT_INITIAL_STATE },
 +	{ "challenge_only",	PAM_OPT_CHALLENGE_ONLY },
  	{ NULL, 0 }
  };
  
 +struct state_items {
 +	void	*item;
 +	size_t	 len;
 +};
 +
 +#define	MAX_STATE_ITEMS		10
  #define	MAX_CHALLENGE_MSGS	10
  #define	PASSWORD_PROMPT		"RADIUS Password:"
  
  static int	 build_access_request(struct rad_handle *, const char *,
 -		    const char *, const char *, const void *, size_t);
 -static int	 do_accept(pam_handle_t *, struct rad_handle *);
 +		    const char *, const char *, const struct state_items *);
 +static int	 do_accept(pam_handle_t *, struct rad_handle *, const char *);
  static int	 do_challenge(pam_handle_t *, struct rad_handle *,
  		    const char *);
  
 @@ -79,9 +91,10 @@
   */
  static int
  build_access_request(struct rad_handle *radh, const char *user,
 -    const char *pass, const char *nas_id, const void *state, size_t state_len)
 +    const char *pass, const char *nas_id, const struct state_items *states)
  {
  	char	 host[MAXHOSTNAMELEN];
 +	int	 state_index = 0;
  
  	if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) {
  		syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh));
 @@ -98,10 +111,17 @@
  		syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh));
  		return (-1);
  	}
 -	if (state != NULL && rad_put_attr(radh, RAD_STATE, state,
 -	    state_len) == -1) {
 -		syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh));
 -		return (-1);
 +	if (states != NULL) {
 +		while (states[state_index].item != NULL) {
 +			if (rad_put_attr(radh, RAD_STATE,
 +				    states[state_index].item,
 +				    states[state_index].len) == -1) {
 +				syslog(LOG_CRIT, "rad_put_attr: %s",
 +				    rad_strerror(radh));
 +				return (-1);
 +			}
 +			++state_index;
 +		}
  	}
  	if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) {
  		syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh));
 @@ -111,23 +131,40 @@
  }
  
  static int
 -do_accept(pam_handle_t *pamh, struct rad_handle *radh)
 +do_accept(pam_handle_t *pamh, struct rad_handle *radh, const char *suffix)
  {
  	int attrtype;
  	const void *attrval;
  	size_t attrlen;
 -	char *s;
 +	char *s, *t, *p;
  
  	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
 -		if (attrtype == RAD_USER_NAME) {
 +		switch (attrtype) {
 +		case RAD_USER_NAME:
  			s = rad_cvt_string(attrval, attrlen);
  			if (s == NULL) {
  				syslog(LOG_CRIT,
  				    "rad_cvt_string: out of memory");
  				return (-1);
  			}
 +			if (suffix) {
 +				t = s + (strlen(s) - strlen(suffix));
 +				if (strcmp(t, suffix) == 0) {
 +					p = malloc(t + 1 - s);
 +					if (p == NULL) {
 +						syslog(LOG_CRIT,
 +						 "do_accept: malloc: %m");
 +						return (-1);
 +					}
 +					strncpy(p, s, (t-s));
 +					p[t-s] = '\0';
 +					free(s);
 +					s = p;
 +				}
 +			}
  			pam_set_item(pamh, PAM_USER, s);
  			free(s);
 +			break;
  		}
  	}
  	if (attrtype == -1) {
 @@ -144,8 +181,8 @@
  	int attrtype;
  	const void *attrval;
  	size_t attrlen;
 -	const void *state;
 -	size_t statelen;
 +	struct state_items states[MAX_STATE_ITEMS+1];
 +	int num_states;
  	struct pam_message msgs[MAX_CHALLENGE_MSGS];
  	const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS];
  	struct pam_response *resp;
 @@ -153,15 +190,21 @@
  	const void *item;
  	const struct pam_conv *conv;
  
 -	state = NULL;
 -	statelen = 0;
 +	memset(states, 0, sizeof(states));
 +	num_states = 0;
  	num_msgs = 0;
  	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
  		switch (attrtype) {
  
  		case RAD_STATE:
 -			state = attrval;
 -			statelen = attrlen;
 +			if (num_states >= MAX_STATE_ITEMS) {
 +				syslog(LOG_ERR,
 +				    "Too many RADIUS state fields in response");
 +				return (PAM_SERVICE_ERR);
 +			}
 +			states[num_states].item = (void *)attrval;
 +			states[num_states].len = (size_t)attrlen;
 +			++num_states;
  			break;
  
  		case RAD_REPLY_MESSAGE:
 @@ -206,7 +249,7 @@
  	    conv->appdata_ptr)) != PAM_SUCCESS)
  		return (retval);
  	if (build_access_request(radh, user, resp[num_msgs-1].resp, NULL,
 -	    state, statelen) == -1)
 +	    states) == -1)
  		return (PAM_SERVICE_ERR);
  	memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp));
  	free(resp[num_msgs-1].resp);
 @@ -222,8 +265,10 @@
  {
  	struct options options;
  	struct rad_handle *radh;
 -	const char *user, *tmpuser, *pass;
 +	struct state_items *states;
 +	const char *user, *authuser, *tmpuser, *pass;
  	char *conf_file, *template_user, *nas_id;
 +	char *user_suffix, *initial_state, *challenge_only;
  	int retval;
  	int e;
  
 @@ -237,6 +282,22 @@
  	pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user);
  	nas_id = NULL;
  	pam_test_option(&options, PAM_OPT_NAS_ID, &nas_id);
 +	user_suffix = NULL;
 +	pam_test_option(&options, PAM_OPT_USER_SUFFIX, &user_suffix);
 +	initial_state = NULL;
 +	pam_test_option(&options, PAM_OPT_INITIAL_STATE, &initial_state);
 +	challenge_only = NULL;
 +	pam_test_option(&options, PAM_OPT_CHALLENGE_ONLY, &challenge_only);
 +
 +	if (initial_state) {
 +		states = calloc(2, sizeof(struct state_items));
 +		states[0].item = initial_state;
 +		states[0].len = strlen(initial_state);
 +		states[1].item = NULL;
 +		states[1].len = 0;
 +	} else {
 +		states = NULL;
 +	}
  
  	retval = pam_get_user(pamh, &user, NULL);
  	if (retval != PAM_SUCCESS)
 @@ -244,11 +305,33 @@
  
  	PAM_LOG("Got user: %s", user);
  
 -	retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
 -	if (retval != PAM_SUCCESS)
 -		return (retval);
 +	/* slight memory leak; one-shot, minimal and tolerable */
 +	if (user_suffix && strlen(user_suffix)) {
 +		size_t sz;
 +		char *p;
 +
 +		sz = strlen(user) + strlen(user_suffix) + 1;
 +		p = malloc(sz);
 +		if (!p) {
 +			syslog(LOG_CRIT, "malloc failed: %m");
 +			return (PAM_SERVICE_ERR);
 +		}
 +		sprintf(p, "%s%s", user, user_suffix);
 +		authuser = p;
 +	} else {
 +		authuser = user;
 +	}
  
 -	PAM_LOG("Got password");
 +	pass = NULL;
 +	if (challenge_only) {
 +		pass = "";
 +	} else {
 +		retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
 +		if (retval != PAM_SUCCESS)
 +			return (retval);
 +
 +		PAM_LOG("Got password");
 +	}
  
  	radh = rad_open();
  	if (radh == NULL) {
 @@ -266,7 +349,7 @@
  
  	PAM_LOG("Radius config file read");
  
 -	if (build_access_request(radh, user, pass, nas_id, NULL, 0) == -1) {
 +	if (build_access_request(radh, authuser, pass, nas_id, states) == -1) {
  		rad_close(radh);
  		return (PAM_SERVICE_ERR);
  	}
 @@ -277,7 +360,7 @@
  		switch (rad_send_request(radh)) {
  
  		case RAD_ACCESS_ACCEPT:
 -			e = do_accept(pamh, radh);
 +			e = do_accept(pamh, radh, user_suffix);
  			rad_close(radh);
  			if (e == -1)
  				return (PAM_SERVICE_ERR);
 @@ -311,6 +394,7 @@
  			return (PAM_AUTH_ERR);
  
  		case RAD_ACCESS_CHALLENGE:
 +			PAM_LOG("Have RAD_ACCESS_CHALLENGE packet");
  			retval = do_challenge(pamh, radh, user);
  			if (retval != PAM_SUCCESS) {
  				rad_close(radh);
Responsible-Changed-From-To: freebsd-bugs->des 
Responsible-Changed-By: des 
Responsible-Changed-When: Mon Feb 23 10:33:54 PST 2004 
Responsible-Changed-Why:  
PAM is mine. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=62885 

From: des@des.no (Dag-Erling =?iso-8859-1?q?Sm=F8rgrav?=)
To: Phil Pennock <pdp+freebsd@nl.demon.net>
Cc: FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/62885: pam_radius doesn't maintain multiple state fields
Date: Mon, 23 Feb 2004 19:30:55 +0100

 Phil Pennock <pdp+freebsd@nl.demon.net> writes:
 > Problem present in, and patch for:
 >   src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.2.6.1
 > Problem still present in:
 >   src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.19
 
 Please submit patches relative to -CURRENT instead of -STABLE.
 
 DES
 --=20
 Dag-Erling Sm=F8rgrav - des@des.no

From: Phil Pennock <pdp@nl.demon.net>
To: Dag-Erling =?iso-8859-1?Q?Sm=F8rgrav?= <des@des.no>
Cc: FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/62885: pam_radius doesn't maintain multiple state fields
Date: Tue, 24 Feb 2004 00:01:12 +0100

 On 2004-02-23 at 19:30 +0100, Dag-Erling Smrgrav wrote:
 > Phil Pennock <pdp+freebsd@nl.demon.net> writes:
 > > Problem present in, and patch for:
 > >   src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.2.6.1
 > > Problem still present in:
 > >   src/lib/libpam/modules/pam_radius/pam_radius.c,v 1.19
 > 
 > Please submit patches relative to -CURRENT instead of -STABLE.
 
 I confirmed the presence of the bug in current, to ensure that I
 wouldn't be wasting anyone's time.  However, I do not track -CURRENT,
 nor do I have the time to do so.  I'm sorry, but that's simply not
 practicable.
 
 Depending upon my workload, I can probably arrange patches against
 RELENG_5_2, which I have on my personal workstation.  But all of our
 production servers run the current recommended stable branches.
 
 When I do this, I'll probably tune the work to have a default domain and
 a domain separator, instead of the crude suffix mechanism which I
 supplied before.
 
 Thanks,
 -- 
 Phil Pennock,  Senior Systems Administrator,  Demon Internet Netherlands
 NL Sales: +31 20 422 20 00      Thus Plc      NL Support: 0800 33 6666 8

From: Phil Pennock <pdp@nl.demon.net>
To: bug-followup@FreeBSD.org, pdp+freebsd@nl.demon.net
Cc:  
Subject: Re: bin/62885: pam_radius doesn't maintain multiple state fields
Date: Mon, 06 Jun 2005 14:13:22 +0200

 --=-0TMqRXssm7ssflc9MAHD
 Content-Type: text/plain
 Content-Transfer-Encoding: 7bit
 
 The attached patch applies to revision 1.22 of
  src/lib/libpam/modules/pam_radius/pam_radius.c
 
 This allows the functionality to be applied to HEAD.
 
 --=-0TMqRXssm7ssflc9MAHD
 Content-Description: 
 Content-Disposition: inline; filename=pam_radius.c.new.patch
 Content-Type: text/x-patch; charset=ISO-8859-15
 Content-Transfer-Encoding: 7bit
 
 --- pam_radius.c.old	Fri Jun 25 12:32:45 2004
 +++ pam_radius.c	Tue May 17 00:53:30 2005
 @@ -58,14 +58,23 @@
  #define PAM_OPT_TEMPLATE_USER	"template_user"
  #define PAM_OPT_NAS_ID		"nas_id"
  #define PAM_OPT_NAS_IPADDR	"nas_ipaddr"
 +#define PAM_OPT_USER_SUFFIX	"user_suffix"
 +#define PAM_OPT_INITIAL_STATE	"initial_state"
 +#define PAM_OPT_CHALLENGE_ONLY	"challenge_only"
  
 +#define MAX_STATE_ITEMS		10
  #define	MAX_CHALLENGE_MSGS	10
  #define	PASSWORD_PROMPT		"RADIUS Password:"
  
 +struct state_items {
 +	void	*item;
 +	size_t	 len;
 +};
 +
  static int	 build_access_request(struct rad_handle *, const char *,
 -		    const char *, const char *, const char *, const void *,
 -		    size_t);
 -static int	 do_accept(pam_handle_t *, struct rad_handle *);
 +		    const char *, const char *, const char *,
 +		    const struct state_items *);
 +static int	 do_accept(pam_handle_t *, struct rad_handle *, const char *);
  static int	 do_challenge(pam_handle_t *, struct rad_handle *,
  		    const char *);
  
 @@ -76,9 +85,9 @@
  static int
  build_access_request(struct rad_handle *radh, const char *user,
      const char *pass, const char *nas_id, const char *nas_ipaddr,
 -    const void *state, size_t state_len)
 +    const struct state_items *states)
  {
 -	int error;
 +	int error, state_index;
  	char host[MAXHOSTNAMELEN];
  	struct sockaddr_in *haddr;
  	struct addrinfo hints;
 @@ -122,10 +131,18 @@
  			}
  		}
  	}
 -	if (state != NULL && rad_put_attr(radh, RAD_STATE, state,
 -	    state_len) == -1) {
 -		syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh));
 -		return (-1);
 +	if (states != NULL) {
 +		state_index = 0;
 +		while (states[state_index].item != NULL) {
 +			if (rad_put_attr(radh, RAD_STATE,
 +			    states[state_index].item,
 +			    states[state_index].len) == -1) {
 +				syslog(LOG_CRIT, "rad_put_attr: %s",
 +				rad_strerror(radh));
 +				return (-1);
 +			}
 +			++state_index;
 +		}
  	}
  	if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) {
  		syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh));
 @@ -135,23 +152,40 @@
  }
  
  static int
 -do_accept(pam_handle_t *pamh, struct rad_handle *radh)
 +do_accept(pam_handle_t *pamh, struct rad_handle *radh, const char *suffix)
  {
  	int attrtype;
  	const void *attrval;
  	size_t attrlen;
 -	char *s;
 +	char *s, *t, *p;
  
  	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
 -		if (attrtype == RAD_USER_NAME) {
 +		switch (attrtype) {
 +		case RAD_USER_NAME:
  			s = rad_cvt_string(attrval, attrlen);
  			if (s == NULL) {
  				syslog(LOG_CRIT,
  				    "rad_cvt_string: out of memory");
  				return (-1);
  			}
 +			if (suffix) {
 +				t = s + (strlen(s) - strlen(suffix));
 +				if (strcmp(t, suffix) == 0) {
 +					p = malloc(t + 1 - s);
 +					if (p == NULL) {
 +						syslog(LOG_CRIT,
 +						 "do_accept: malloc: %m");
 +						return (-1);
 +					}
 +					strncpy(p, s, (t-s));
 +					p[t-s] = '\0';
 +					free(s);
 +					s = p;
 +				}
 +			}
  			pam_set_item(pamh, PAM_USER, s);
  			free(s);
 +			break;
  		}
  	}
  	if (attrtype == -1) {
 @@ -168,8 +202,8 @@
  	int attrtype;
  	const void *attrval;
  	size_t attrlen;
 -	const void *state;
 -	size_t statelen;
 +	struct state_items states[MAX_STATE_ITEMS+1];
 +	int num_states;
  	struct pam_message msgs[MAX_CHALLENGE_MSGS];
  	const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS];
  	struct pam_response *resp;
 @@ -177,15 +211,21 @@
  	const void *item;
  	const struct pam_conv *conv;
  
 -	state = NULL;
 -	statelen = 0;
 +	memset(states, 0, sizeof(states));
 +	num_states = 0;
  	num_msgs = 0;
  	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
  		switch (attrtype) {
  
  		case RAD_STATE:
 -			state = attrval;
 -			statelen = attrlen;
 +			if (num_states >= MAX_STATE_ITEMS) {
 +				syslog(LOG_ERR,
 +				 "Too many RADIUS state fields in response");
 +				return (PAM_SERVICE_ERR);
 +			}
 +			states[num_states].item = (void *)attrval;
 +			states[num_states].len = (size_t)attrlen;
 +			++num_states;
  			break;
  
  		case RAD_REPLY_MESSAGE:
 @@ -230,7 +270,7 @@
  	    conv->appdata_ptr)) != PAM_SUCCESS)
  		return (retval);
  	if (build_access_request(radh, user, resp[num_msgs-1].resp, NULL,
 -	    NULL, state, statelen) == -1)
 +	    NULL, states) == -1)
  		return (PAM_SERVICE_ERR);
  	memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp));
  	free(resp[num_msgs-1].resp);
 @@ -245,9 +285,11 @@
      int argc __unused, const char *argv[] __unused)
  {
  	struct rad_handle *radh;
 -	const char *user, *pass;
 +	struct state_items *states;
 +	const char *user, *authuser, *pass;
  	const void *tmpuser;
  	const char *conf_file, *template_user, *nas_id, *nas_ipaddr;
 +	const char *user_suffix, *initial_state, *challenge_only;
  	int retval;
  	int e;
  
 @@ -255,6 +297,20 @@
  	template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER);
  	nas_id = openpam_get_option(pamh, PAM_OPT_NAS_ID);
  	nas_ipaddr = openpam_get_option(pamh, PAM_OPT_NAS_IPADDR);
 +	user_suffix = openpam_get_option(pamh, PAM_OPT_USER_SUFFIX);
 +	initial_state = openpam_get_option(pamh, PAM_OPT_INITIAL_STATE);
 +	challenge_only = openpam_get_option(pamh, PAM_OPT_CHALLENGE_ONLY);
 +
 +	if (initial_state) {
 +		states = calloc(2, sizeof(struct state_items));
 +		/* the states as passed are never modified so lose const */
 +		states[0].item = (void *)initial_state;
 +		states[0].len = strlen(initial_state);
 +		states[1].item = NULL;
 +		states[1].len = 0;
 +	} else {
 +		states = NULL;
 +	}
  
  	retval = pam_get_user(pamh, &user, NULL);
  	if (retval != PAM_SUCCESS)
 @@ -262,11 +318,34 @@
  
  	PAM_LOG("Got user: %s", user);
  
 -	retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT);
 -	if (retval != PAM_SUCCESS)
 -		return (retval);
 +	/* slight memory leak; one-shot, minimal and tolerable */
 +	if (user_suffix && strlen(user_suffix)) {
 +		size_t sz;
 +		char *p;
 +
 +		sz = strlen(user) + strlen(user_suffix) + 1;
 +		p = malloc(sz);
 +		if (!p) {
 +			syslog(LOG_CRIT, "malloc failed: %m");
 +			return (PAM_SERVICE_ERR);
 +		}
 +		sprintf(p, "%s%s", user, user_suffix);
 +		authuser = p;
 +	} else {
 +		authuser = user;
 +	}
 +
 +	pass = NULL;
 +	if (challenge_only) {
 +		pass = "";
 +	} else {
 +		retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass,
 +						PASSWORD_PROMPT);
 +		if (retval != PAM_SUCCESS)
 +			return (retval);
  
 -	PAM_LOG("Got password");
 +		PAM_LOG("Got password");
 +	}
  
  	radh = rad_open();
  	if (radh == NULL) {
 @@ -284,8 +363,8 @@
  
  	PAM_LOG("Radius config file read");
  
 -	if (build_access_request(radh, user, pass, nas_id, nas_ipaddr, NULL,
 -	    0) == -1) {
 +	if (build_access_request(radh, user, pass, nas_id, nas_ipaddr,
 +	    states) == -1) {
  		rad_close(radh);
  		return (PAM_SERVICE_ERR);
  	}
 @@ -296,7 +375,7 @@
  		switch (rad_send_request(radh)) {
  
  		case RAD_ACCESS_ACCEPT:
 -			e = do_accept(pamh, radh);
 +			e = do_accept(pamh, radh, user_suffix);
  			rad_close(radh);
  			if (e == -1)
  				return (PAM_SERVICE_ERR);
 @@ -329,6 +408,7 @@
  			return (PAM_AUTH_ERR);
  
  		case RAD_ACCESS_CHALLENGE:
 +			PAM_LOG("Have RAD_ACCESS_CHALLENGE packet");
  			retval = do_challenge(pamh, radh, user);
  			if (retval != PAM_SUCCESS) {
  				rad_close(radh);
 
 --=-0TMqRXssm7ssflc9MAHD--
 

From: Phil Pennock <pdp@nl.demon.net>
To: bug-followup@FreeBSD.org, pdp+freebsd@nl.demon.net
Cc:  
Subject: Re: bin/62885: pam_radius doesn't maintain multiple state fields
Date: Fri, 5 Aug 2005 11:59:00 +0200

 This ticket should either be reclassified as a change request or closed,
 as appropriate.
 
 The claim that the support for just one state field is a bug is wrong.
 As such, the "sw-bug" status is bogus, and my fault.  I'm sorry.
 
 I've just read the RFC's stance on State fields, and it's a zero-or-one
 instance option.  Use of more than one State field is a non-standard
 extension.  I should have read the RFC first instead of believing what I
 was told.  :^(
 
 I'm sorry for wasting peoples' time.
 
 However, the extension is fairly harmless and it might still be
 appropriate as a change-request, but no matter if not.  We'll maintain
 the patch locally as a site-specific extension.
 -- 
 Phil Pennock,       Senior Systems Administrator,      Demon Netherlands
 NL Sales: +31 20 422 20 00      Thus Plc      NL Support: 0800 33 6666 8
State-Changed-From-To: open->feedback 
State-Changed-By: remko 
State-Changed-When: Mon Feb 11 21:03:44 UTC 2008 
State-Changed-Why:  
Hello, would it be possible to send a recent version of this? In 
addition: Dag-Erling, is there interest in this at all, can you work on 
this? 

http://www.freebsd.org/cgi/query-pr.cgi?pr=62885 

From: Phil Pennock <freebsd-bugs@spodhuis.org>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/62885: pam_radius(8) doesn't maintain multiple state fields
Date: Mon, 31 Jan 2011 20:53:27 -0500

 Oops, sorry, the email address used to file this bug is defunct, the
 company no longer exists. 
 
 I no longer have a RADIUS setup or client implementation in which to
 develop the feature, sorry.  Unless someone speaks up and still wants
 this, I think it's safe to close as a WONTFIX issue.
>Unformatted:
