bin/62885: pam_radius doesn't maintain multiple state fields

Phil Pennock pdp at nl.demon.net
Mon Feb 16 10:10:27 PST 2004


The following reply was made to PR bin/62885; it has been noted by GNATS.

From: Phil Pennock <pdp at nl.demon.net>
To: FreeBSD-gnats-submit at FreeBSD.org, freebsd-bugs at 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 at 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);


More information about the freebsd-bugs mailing list