summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan DeKok <aland@freeradius.org>2017-02-08 13:15:18 -0500
committerGitHub <noreply@github.com>2017-02-08 13:15:18 -0500
commit2ab3b77e3e15f73dc206906737d2f7255e166274 (patch)
treeaa6902c795e46cc22a27114b88b6c2a936bf24df
parent3a8d8f067e56d844be0df0ea024d6e5bca10481e (diff)
parent306401ef5fdbaa4b623e7947cc83df680a783964 (diff)
downloadlibpam-radius-auth-2ab3b77e3e15f73dc206906737d2f7255e166274.tar.gz
libpam-radius-auth-2ab3b77e3e15f73dc206906737d2f7255e166274.zip
Merge pull request #18 from samuelvarley/master
Make module (almost) thread-safe.
-rw-r--r--src/pam_radius_auth.c226
-rw-r--r--src/pam_radius_auth.h73
2 files changed, 137 insertions, 162 deletions
diff --git a/src/pam_radius_auth.c b/src/pam_radius_auth.c
index 34b2a38..471c0d6 100644
--- a/src/pam_radius_auth.c
+++ b/src/pam_radius_auth.c
@@ -55,25 +55,12 @@
#define PAM_SM_PASSWORD
#define PAM_SM_SESSION
-#include <limits.h>
-#include <errno.h>
-#include <sys/time.h>
-
#include "pam_radius_auth.h"
-#define DPRINT if (opt_debug & PAM_DEBUG_ARG) _pam_log
+#define DPRINT if (debug) _pam_log
/* internal data */
static CONST char *pam_module_name = "pam_radius_auth";
-static char conf_file[BUFFER_SIZE]; /* configuration file */
-static int opt_debug = FALSE; /* print debug info */
-
-/* we need to save these from open_session to close_session, since
- * when close_session will be called we won't be root anymore and
- * won't be able to access again the radius server configuration file
- * -- cristiang */
-static radius_server_t *live_server = NULL;
-static time_t session_time;
/* logging */
static void _pam_log(int err, CONST char *format, ...)
@@ -95,7 +82,7 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf)
memset(conf, 0, sizeof(radius_conf_t)); /* ensure it's initialized */
- strcpy(conf_file, CONF_FILE);
+ conf->conf_file = CONF_FILE;
/* set the default prompt */
snprintf(conf->prompt, MAXPROMPT, "%s: ", DEFAULT_PROMPT);
@@ -112,13 +99,7 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf)
/* generic options */
if (!strncmp(*argv,"conf=",5)) {
- /* protect against buffer overflow */
- if (strlen(*argv+5) >= sizeof(conf_file)) {
- _pam_log(LOG_ERR, "conf= argument too long");
- conf_file[0] = 0;
- return 0;
- }
- strcpy(conf_file,*argv+5);
+ conf->conf_file = *argv+5;
} else if (!strcmp(*argv, "use_first_pass")) {
ctrl |= PAM_USE_FIRST_PASS;
@@ -149,8 +130,7 @@ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf)
} else if (!strcmp(*argv, "debug")) {
ctrl |= PAM_DEBUG_ARG;
- conf->debug = 1;
- opt_debug = TRUE;
+ conf->debug = TRUE;
} else if (!strncmp(*argv, "prompt=", 7)) {
if (!strncmp(conf->prompt, (char*)*argv+7, MAXPROMPT)) {
@@ -190,98 +170,79 @@ void _int_free(pam_handle_t * pamh, void *x, int error_status)
*************************************************************************/
/*
- * Return an IP address in host long notation from
- * one supplied in standard dot notation.
+ * A strerror_r() wrapper function to deal with its nuisances.
*/
-static uint32_t ipstr2long(char *ip_str) {
- char buf[6];
- char *ptr;
- int i;
- int count;
- uint32_t ipaddr;
- int cur_byte;
-
- ipaddr = (uint32_t)0;
-
- for(i = 0;i < 4;i++) {
- ptr = buf;
- count = 0;
- *ptr = '\0';
-
- while(*ip_str != '.' && *ip_str != '\0' && count < 4) {
- if (!isdigit((unsigned char)*ip_str)) {
- return (uint32_t)0;
- }
- *ptr++ = *ip_str++;
- count++;
- }
-
- if (count >= 4 || count == 0) {
- return (uint32_t)0;
- }
-
- *ptr = '\0';
- cur_byte = atoi(buf);
- if (cur_byte < 0 || cur_byte > 255) {
- return (uint32_t)0;
- }
-
- ip_str++;
- ipaddr = ipaddr << 8 | (uint32_t)cur_byte;
+static void get_error_string(int errnum, char *buf, size_t buflen) {
+#if !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE)
+ /* XSI version of strerror_r(). */
+ int retval = strerror_r(errnum, buf, buflen);
+
+ /* POSIX does not state what will happen to the buffer if the function fails.
+ * Put it into a known state rather than leave it possibly uninitialized. */
+ if (retval != 0 && buflen > (size_t)0) {
+ buf[0] = '\0';
}
- return ipaddr;
+#else
+ /* GNU version of strerror_r(). */
+ char tmp_buf[BUFFER_SIZE];
+ char *retval = strerror_r(errnum, tmp_buf, sizeof(tmp_buf));
+
+ snprintf(buf, buflen, "%s", retval);
+#endif
}
/*
- * Check for valid IP address in standard dot notation.
+ * Return an IP address in host long notation from a host
+ * name or address in dot notation.
*/
-static int good_ipaddr(char *addr) {
- int dot_count;
- int digit_count;
-
- dot_count = 0;
- digit_count = 0;
- while(*addr != '\0' && *addr != ' ') {
- if (*addr == '.') {
- dot_count++;
- digit_count = 0;
- } else if (!isdigit((unsigned char)*addr)) {
- dot_count = 5;
- } else {
- digit_count++;
- if (digit_count > 3) {
- dot_count = 5;
- }
- }
- addr++;
- }
- if (dot_count != 3) {
- return -1;
+static uint32_t get_ipaddr(char *host) {
+ struct addrinfo hints;
+ struct addrinfo *results;
+ uint32_t addr;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if (getaddrinfo(host, NULL, &hints, &results) == 0) {
+ struct sockaddr_in *sockaddr = (struct sockaddr_in *)results->ai_addr;
+ addr = ntohl(sockaddr->sin_addr.s_addr);
+ freeaddrinfo(results);
} else {
- return 0;
+ addr = (uint32_t)0;
}
+
+ return addr;
}
/*
- * Return an IP address in host long notation from a host
- * name or address in dot notation.
+ * Gets the UDP port number associated with a service name.
+ * The port number is returned in network byte order.
*/
-static uint32_t get_ipaddr(char *host) {
- struct hostent *hp;
-
- if (good_ipaddr(host) == 0) {
- return ipstr2long(host);
- } else if ((hp = gethostbyname(host)) == (struct hostent *)NULL) {
- return (uint32_t)0;
+static uint16_t get_udp_port(char *service) {
+ struct addrinfo hints;
+ struct addrinfo *results;
+ uint16_t port;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if (getaddrinfo(NULL, service, &hints, &results) == 0) {
+ struct sockaddr_in *sockaddr = (struct sockaddr_in *)results->ai_addr;
+ port = sockaddr->sin_port;
+ freeaddrinfo(results);
+ } else {
+ port = (uint16_t)0;
}
- return ntohl(*(uint32_t *)hp->h_addr);
+ return port;
}
/*
* take server->hostname, and convert it to server->ip and server->port
*/
-static int host2server(radius_server_t *server)
+static int host2server(int debug, radius_server_t *server)
{
char *p;
@@ -307,29 +268,25 @@ static int host2server(radius_server_t *server)
server->port = htons((uint16_t) (i + 1));
}
} else { /* the port looks like it's a name */
- struct servent *svp;
-
if (p) { /* maybe it's not "radius" */
- svp = getservbyname (p, "udp");
+ server->port = get_udp_port(p);
/* quotes allow distinction from above, lest p be radius or radacct */
- DPRINT(LOG_DEBUG, "DEBUG: getservbyname('%s', udp) returned %p.\n", p, svp);
+ DPRINT(LOG_DEBUG, "DEBUG: get_udp_port('%s') returned %u.\n", p, server->port);
*(--p) = ':'; /* be sure to put the delimiter back */
} else {
if (!server->accounting) {
- svp = getservbyname ("radius", "udp");
- DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radius, udp) returned %p.\n", svp);
+ server->port = get_udp_port("radius");
+ DPRINT(LOG_DEBUG, "DEBUG: get_udp_port(radius) returned %u.\n", server->port);
} else {
- svp = getservbyname ("radacct", "udp");
- DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radacct, udp) returned %p.\n", svp);
+ server->port = get_udp_port("radacct");
+ DPRINT(LOG_DEBUG, "DEBUG: get_udp_port(radacct) returned %u.\n", server->port);
}
}
- if (svp == (struct servent *) 0) {
+ if (!server->port) {
/* debugging above... */
return PAM_AUTHINFO_UNAVAIL;
}
-
- server->port = svp->s_port;
}
}
@@ -608,9 +565,11 @@ static int initialize(radius_conf_t *conf, int accounting)
char src_ip[MAX_IP_LEN];
/* the first time around, read the configuration file */
- if ((fserver = fopen (conf_file, "r")) == (FILE*)NULL) {
+ if ((fserver = fopen (conf->conf_file, "r")) == (FILE*)NULL) {
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
_pam_log(LOG_ERR, "Could not open configuration file %s: %s\n",
- conf_file, strerror(errno));
+ conf->conf_file, error_string);
return PAM_ABORT;
}
@@ -641,7 +600,7 @@ static int initialize(radius_conf_t *conf, int accounting)
src_ip[0] = 0;
if (sscanf(p, "%s %s %d %s", hostname, secret, &timeout, src_ip) < 2) {
_pam_log(LOG_ERR, "ERROR reading %s, line %d: Could not read hostname or secret\n",
- conf_file, line);
+ conf->conf_file, line);
continue; /* invalid line */
} else { /* read it in and save the data */
radius_server_t *tmp;
@@ -673,14 +632,16 @@ static int initialize(radius_conf_t *conf, int accounting)
if (!server) { /* no server found, die a horrible death */
_pam_log(LOG_ERR, "No RADIUS server found in configuration file %s\n",
- conf_file);
+ conf->conf_file);
return PAM_AUTHINFO_UNAVAIL;
}
/* open a socket. Dies if it fails */
conf->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (conf->sockfd < 0) {
- _pam_log(LOG_ERR, "Failed to open RADIUS socket: %s\n", strerror(errno));
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
+ _pam_log(LOG_ERR, "Failed to open RADIUS socket: %s\n", error_string);
return PAM_AUTHINFO_UNAVAIL;
}
@@ -697,7 +658,9 @@ static int initialize(radius_conf_t *conf, int accounting)
if (bind(conf->sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) {
- _pam_log(LOG_ERR, "Failed binding to port: %s", strerror(errno));
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
+ _pam_log(LOG_ERR, "Failed binding to port: %s", error_string);
close(conf->sockfd);
return PAM_AUTHINFO_UNAVAIL;
}
@@ -742,13 +705,7 @@ static void build_radius_packet(AUTH_HDR *request, CONST char *user, CONST char
if ((conf->server->ip.s_addr == ntohl(0x7f000001)) || (!hostname[0])) {
ipaddr = 0x7f000001;
} else {
- struct hostent *hp;
-
- if ((hp = gethostbyname(hostname)) == (struct hostent *) NULL) {
- ipaddr = 0x00000000; /* no client IP address */
- } else {
- ipaddr = ntohl(*(uint32_t *) hp->h_addr); /* use the first one available */
- }
+ ipaddr = get_ipaddr(hostname);
}
/* If we can't find an IP address, then don't add one */
@@ -809,7 +766,7 @@ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *respons
memset(response, 0, sizeof(AUTH_HDR));
/* only look up IP information as necessary */
- if ((retval = host2server(server)) != PAM_SUCCESS) {
+ if ((retval = host2server(conf->debug, server)) != PAM_SUCCESS) {
_pam_log(LOG_ERR,
"Failed looking up IP address for RADIUS server %s (errcode=%d)",
server->hostname, retval);
@@ -833,8 +790,10 @@ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *respons
/* send the packet */
if (sendto(conf->sockfd, (char *) request, total_length, 0,
&saremote, sizeof(struct sockaddr_in)) < 0) {
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
_pam_log(LOG_ERR, "Error sending RADIUS packet to server %s: %s",
- server->hostname, strerror(errno));
+ server->hostname, error_string);
ok = FALSE;
goto next; /* skip to the next server */
}
@@ -883,8 +842,10 @@ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *respons
}
} else { /* not an interrupt, it was a real error */
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
_pam_log(LOG_ERR, "Error waiting for response from RADIUS server %s: %s",
- server->hostname, strerror(errno));
+ server->hostname, error_string);
ok = FALSE;
break;
}
@@ -895,8 +856,10 @@ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *respons
/* try to receive some data */
if ((total_length = recvfrom(conf->sockfd, (void *) response, BUFFER_SIZE,
0, &saremote, &salen)) < 0) {
+ char error_string[BUFFER_SIZE];
+ get_error_string(errno, error_string, sizeof(error_string));
_pam_log(LOG_ERR, "error reading RADIUS packet from server %s: %s",
- server->hostname, strerror(errno));
+ server->hostname, error_string);
ok = FALSE;
break;
@@ -996,7 +959,6 @@ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *respons
/* we've found one that does respond, forget about the other servers */
cleanup(server->next);
server->next = NULL;
- live_server = server; /* we've got a live one! */
break;
}
}
@@ -1077,6 +1039,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,CONST c
CONST char *rhost;
char *resp2challenge = NULL;
int ctrl;
+ int debug;
int retval = PAM_AUTH_ERR;
int num_challenge = 0;
@@ -1087,6 +1050,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,CONST c
radius_conf_t config;
ctrl = _pam_parse(argc, argv, &config);
+ debug = config.debug;
/* grab the user name */
retval = pam_get_user(pamh, &user, NULL);
@@ -1363,9 +1327,15 @@ static int pam_private_session(pam_handle_t *pamh, int flags, int argc, CONST ch
add_int_attribute(request, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS);
if (status == PW_STATUS_START) {
- session_time = time(NULL);
+ time_t *session_time = malloc(sizeof(time_t));
+ time(session_time);
+ pam_set_data(pamh, "rad_session_time", (void *) session_time, _int_free);
} else {
- add_int_attribute(request, PW_ACCT_SESSION_TIME, time(NULL) - session_time);
+ time_t *session_time;
+ retval = pam_get_data(pamh, "rad_session_time", (CONST void **) &session_time);
+ PAM_FAIL_CHECK;
+
+ add_int_attribute(request, PW_ACCT_SESSION_TIME, time(NULL) - *session_time);
}
retval = talk_radius(&config, request, response, NULL, NULL, 1);
diff --git a/src/pam_radius_auth.h b/src/pam_radius_auth.h
index 95f262c..0882c53 100644
--- a/src/pam_radius_auth.h
+++ b/src/pam_radius_auth.h
@@ -3,6 +3,9 @@
#include "config.h"
+#include <limits.h>
+#include <errno.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
@@ -44,40 +47,6 @@
#define MAXPROMPT 33 /* max prompt length, including '\0' */
#define DEFAULT_PROMPT "Password" /* default prompt, without the ': ' */
-/*************************************************************************
- * Additional RADIUS definitions
- *************************************************************************/
-
-/* Per-attribute structure */
-typedef struct attribute_t {
- unsigned char attribute;
- unsigned char length;
- unsigned char data[1];
-} attribute_t;
-
-typedef struct radius_server_t {
- struct radius_server_t *next;
- struct in_addr ip;
- uint16_t port;
- char *hostname;
- char *secret;
- int timeout;
- int accounting;
-} radius_server_t;
-
-typedef struct radius_conf_t {
- radius_server_t *server;
- int retries;
- int localifdown;
- char *client_id;
- int accounting_bug;
- int force_prompt;
- int max_challenge;
- int sockfd;
- int debug;
- char prompt[MAXPROMPT];
-} radius_conf_t;
-
/*************************************************************************
* Platform specific defines
@@ -143,4 +112,40 @@ typedef struct radius_conf_t {
#define TRUE !FALSE
#endif
+
+/*************************************************************************
+ * Additional RADIUS definitions
+ *************************************************************************/
+
+/* Per-attribute structure */
+typedef struct attribute_t {
+ unsigned char attribute;
+ unsigned char length;
+ unsigned char data[1];
+} attribute_t;
+
+typedef struct radius_server_t {
+ struct radius_server_t *next;
+ struct in_addr ip;
+ uint16_t port;
+ char *hostname;
+ char *secret;
+ int timeout;
+ int accounting;
+} radius_server_t;
+
+typedef struct radius_conf_t {
+ radius_server_t *server;
+ int retries;
+ int localifdown;
+ char *client_id;
+ int accounting_bug;
+ int force_prompt;
+ int max_challenge;
+ int sockfd;
+ int debug;
+ CONST char *conf_file;
+ char prompt[MAXPROMPT];
+} radius_conf_t;
+
#endif /* PAM_RADIUS_H */