diff --git a/NEWS b/NEWS index b627ef68..a2754a71 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ * Version 1.3.1 (unreleased) +- When using RADIUS authentication through the PAM module, take into + account RADIUS attributes that PAM passes as environment variables. - Fixed issues with PAM authentication when combined with pam_sssd (#618) diff --git a/src/auth/pam.c b/src/auth/pam.c index 43ab8004..dd6bc1e2 100644 --- a/src/auth/pam.c +++ b/src/auth/pam.c @@ -41,12 +41,13 @@ * no session management. */ +#include "auth/pam.h" +#include "auth-unix.h" #include #include #include #include -#include "auth/pam.h" -#include "auth-unix.h" +#include #define PAM_STACK_SIZE (1024*1024) @@ -138,9 +139,14 @@ static void co_auth_user(void* data) { struct pam_ctx_st * pctx = data; int pret; +static const char envname[] = "Framed-IP-Address"; +const char *envval; pctx->state = PAM_S_INIT; + /* Unset environment variables that may be set by pam_authenticate() */ + unsetenv(envname); + pret = pam_authenticate(pctx->ph, 0); if (pret != PAM_SUCCESS) { oc_syslog(LOG_INFO, "PAM authenticate error for '%s': %s", pctx->username, pam_strerror(pctx->ph, pret)); @@ -148,6 +154,41 @@ int pret; goto wait; } + /* + * As of release 2.0.0, the pam_radius module supports passing RADIUS + * attributes as environment variables. Only the Framed-IP-Address attribute + * is currently supported: + * https://github.com/FreeRADIUS/pam_radius/pull/47 + * This is just a hack. We recommend explicitly using authentication RADIUS + * authentication in ocserv, instead of misusing PAM authentication against + * a RADIUS server. + */ + envval = getenv(envname); + if (envval) { + struct in_addr addr; + + oc_syslog(LOG_DEBUG, "Get PAM environment variable: %s=%s\n", envname, envval); + switch (inet_pton(AF_INET, envval, &addr)) { + case 1: + if (addr.s_addr != 0xffffffff && addr.s_addr != 0xfffffffe) { + /* According to RFC2865 the values above instruct the server + * (fe) to assign an address from the pool of the server, and + * (ff) to assign address as negotiated with the client. + * We don't negotiate with clients. + */ + strncpy(pctx->ipv4, envval, MAX_IP_STR - 1); + oc_syslog(LOG_DEBUG, "Interpret PAM environment variable as a RADIUS attribute: %s=%s\n", envname, envval); + } + break; + case 0: + oc_syslog(LOG_NOTICE, "PAM environment variable is not an IPv4 address: %s=%s\n", envname, envval); + break; + default: + oc_syslog(LOG_NOTICE, "Cannot convert to an IPv4 address: %s\n", strerror(errno)); + break; + } + } + pret = pam_acct_mgmt(pctx->ph, 0); if (pret == PAM_NEW_AUTHTOK_REQD) { /* change password */ diff --git a/src/auth/pam.h b/src/auth/pam.h index b850e956..cd5a6011 100644 --- a/src/auth/pam.h +++ b/src/auth/pam.h @@ -26,6 +26,8 @@ #ifdef HAVE_PAM +#include "common/common.h" + #include #include #include @@ -43,6 +45,7 @@ struct pam_ctx_st { str_st msg; str_st prompt; unsigned sent_msg; + char ipv4[MAX_IP_STR]; struct pam_response *replies; /* for safety */ unsigned state; /* PAM_S_ */ unsigned passwd_counter; diff --git a/src/auth/radius.c b/src/auth/radius.c index 4dc6e717..19856701 100644 --- a/src/auth/radius.c +++ b/src/auth/radius.c @@ -432,9 +432,9 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } else if (vp->attribute == PW_FRAMED_IP_ADDRESS && vp->type == PW_TYPE_IPADDR) { /* Framed-IP-Address */ if (vp->lvalue != 0xffffffff && vp->lvalue != 0xfffffffe) { - /* According to RFC2865 the values above (fe) instruct the - * server to assign an address from the pool of the server, - * and (ff) to assign address as negotiated with the client. + /* According to RFC2865 the values above instruct the server + * (fe) to assign an address from the pool of the server, and + * (ff) to assign address as negotiated with the client. * We don't negotiate with clients. */ ipv4 = htonl(vp->lvalue);