+++ /dev/null
-/* $Id: radius.c 7745 2008-04-06 10:18:54Z iulius $
-**
-** Authenticate a user against a remote radius server.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/time.h"
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <netdb.h>
-#include <signal.h>
-
-/* Needed on AIX 4.1 to get fd_set and friends. */
-#if HAVE_SYS_SELECT_H
-# include <sys/select.h>
-#endif
-
-#include "inn/innconf.h"
-#include "inn/md5.h"
-#include "inn/messages.h"
-#include "libinn.h"
-#include "nntp.h"
-#include "paths.h"
-#include "conffile.h"
-
-#include "libauth.h"
-
-#define RADIUS_LOCAL_PORT NNTP_PORT
-
-#define AUTH_VECTOR_LEN 16
-
-typedef struct _auth_req {
- unsigned char code;
- unsigned char id;
- unsigned short length;
- unsigned char vector[AUTH_VECTOR_LEN];
- unsigned char data[NNTP_STRLEN*2];
- int datalen;
-} auth_req;
-
-typedef struct _rad_config_t {
- char *secret; /* pseudo encryption thingy secret that radius uses */
-
- char *radhost; /* parameters for talking to the remote radius sever */
- int radport;
- char *lochost;
- int locport;
-
- char *prefix, *suffix; /* futz with the username, if necessary */
- int ignore_source;
-
- struct _rad_config_t *next; /* point to any additional servers */
-} rad_config_t;
-
-typedef struct _sending_t {
- auth_req req;
- int reqlen;
- struct sockaddr_in sinr;
- struct _sending_t *next;
-} sending_t;
-
-#define RADlbrace 1
-#define RADrbrace 2
-#define RADserver 10
-#define RADhost 11
-#define RADsecret 12
-#define RADport 13
-#define RADlochost 14
-#define RADlocport 15
-#define RADprefix 16
-#define RADsuffix 17
-#define RADsource 18
-
-static CONFTOKEN radtoks[] = {
- { RADlbrace, "{" },
- { RADrbrace, "}" },
- { RADserver, "server" },
- { RADhost, "radhost:" },
- { RADsecret, "secret:" },
- { RADport, "radport:" },
- { RADlochost, "lochost:" },
- { RADlocport, "locport:" },
- { RADprefix, "prefix:" },
- { RADsuffix, "suffix:" },
- { RADsource, "ignore-source:" },
- { 0, 0 }
-};
-
-static rad_config_t *get_radconf(void)
-{
- rad_config_t *new;
-
- new = xcalloc(1, sizeof(rad_config_t));
- new->next = NULL;
-
- return new;
-}
-
-static int read_config(char *authfile, rad_config_t *radconf)
-{
- int inbrace;
- rad_config_t *radconfig=NULL;
- CONFFILE *file;
- CONFTOKEN *token;
- char *server;
- int type;
- char *iter;
-
- if ((file = CONFfopen(authfile)) == NULL)
- sysdie("cannot open config file %s", authfile);
-
- inbrace = 0;
- while ((token = CONFgettoken(radtoks, file)) != NULL) {
- if (!inbrace) {
- if (token->type != RADserver)
- die("expected server keyword on line %d", file->lineno);
- if ((token = CONFgettoken(0, file)) == NULL)
- die("expected server name on line %d", file->lineno);
- server = xstrdup(token->name);
- if ((token = CONFgettoken(radtoks, file)) == NULL
- || token->type != RADlbrace)
- die("expected { on line %d", file->lineno);
- inbrace = 1;
-
- if (radconfig == NULL)
- radconfig = radconf;
- else {
- radconfig->next = get_radconf();
- radconfig = radconfig->next;
- }
- }
- else {
- type = token->type;
- if (type == RADrbrace)
- inbrace = 0;
- else {
- if ((token = CONFgettoken(0, file)) == NULL)
- die("keyword with no value on line %d", file->lineno);
- iter = token->name;
-
- /* what are we setting? */
- switch(type) {
- case RADsecret:
- if (radconfig->secret) continue;
- radconfig->secret = xstrdup(iter);
- break;
- case RADhost:
- if (radconfig->radhost) continue;
- radconfig->radhost = xstrdup(iter);
- break;
- case RADport:
- if (radconfig->radport) continue;
- radconfig->radport = atoi(iter);
- break;
- case RADlochost:
- if (radconfig->lochost) continue;
- radconfig->lochost = xstrdup(iter);
- break;
- case RADlocport:
- if (radconfig->locport) continue;
- radconfig->locport = atoi(iter);
- break;
- case RADprefix:
- if (radconfig->prefix) continue;
- radconfig->prefix = xstrdup(iter);
- break;
- case RADsuffix:
- if (radconfig->suffix) continue;
- radconfig->suffix = xstrdup(iter);
- break;
- case RADsource:
- if (!strcasecmp(iter, "true"))
- radconfig->ignore_source = 1;
- else if (!strcasecmp(iter, "false"))
- radconfig->ignore_source = 0;
- else
- die("expected true or false after ignore-source on line %d",
- file->lineno);
- break;
- default:
- die("unknown keyword on line %d", file->lineno);
- }
- }
- }
- }
-
- CONFfclose(file);
-
- if (!radconf->radhost)
- die("no radius host specified");
- else if (!radconf->secret)
- die("no shared secret with radius host specified");
-
- return(0);
-}
-
-#define PW_AUTH_UDP_PORT 1645
-
-#define PW_AUTHENTICATION_REQUEST 1
-#define PW_AUTHENTICATION_ACK 2
-#define PW_AUTHENTICATION_REJECT 3
-
-#define PW_USER_NAME 1
-#define PW_PASSWORD 2
-
-#define PW_SERVICE_TYPE 6
-#define PW_SERVICE_AUTH_ONLY 8
-
-#define RAD_NAS_IP_ADDRESS 4 /* IP address */
-#define RAD_NAS_PORT 5 /* Integer */
-
-static void req_copyto (auth_req to, sending_t *from)
-{
- to = from->req;
-}
-
-static void req_copyfrom (sending_t *to, auth_req from)
-{
- to->req = from;
-}
-
-static int rad_auth(rad_config_t *radconfig, char *uname, char *pass)
-{
- auth_req req;
- int i, j, jlen, passstart;
- unsigned char secbuf[128];
- char hostname[SMBUF];
- unsigned char digest[MD5_DIGESTSIZE];
- struct timeval seed;
- struct sockaddr_in sinl;
- int sock;
- struct hostent *hent;
- int passlen;
- time_t now, end;
- struct timeval tmout;
- int got;
- fd_set rdfds;
- uint32_t nvalue;
- socklen_t slen;
- int authtries= 3; /* number of times to try reaching the radius server */
- rad_config_t *config;
- sending_t *reqtop, *sreq, *new;
- int done;
-
- /* set up the linked list */
- config = radconfig;
-
- if (config == NULL) {
- warn("no configuration file");
- return(-2);
- } else {
- /* setting sreq to NULL guarantees reqtop will be properly set later */
- sreq = NULL;
- reqtop = NULL;
- }
-
- while (config != NULL){
- new = xmalloc(sizeof(sending_t));
- new->next = NULL;
-
- if (sreq == NULL){
- reqtop = new;
- sreq = new;
- } else {
- sreq->next = new;
- sreq = sreq->next;
- }
- req_copyto(req, sreq);
-
- /* first, build the sockaddrs */
- memset(&sinl, '\0', sizeof(sinl));
- memset(&sreq->sinr, '\0', sizeof(sreq->sinr));
- sinl.sin_family = AF_INET;
- sreq->sinr.sin_family = AF_INET;
- if (config->lochost == NULL) {
- if (gethostname(hostname, sizeof(hostname)) != 0) {
- syswarn("cannot get local hostname");
- return(-2);
- }
- config->lochost = xstrdup(hostname);
- }
- if (config->lochost) {
- if (inet_aton(config->lochost, &sinl.sin_addr) != 1) {
- if ((hent = gethostbyname(config->lochost)) == NULL) {
- warn("cannot gethostbyname lochost %s", config->lochost);
- return(-2);
- }
- memcpy(&sinl.sin_addr.s_addr, hent->h_addr,
- sizeof(struct in_addr));
- }
- }
- if (inet_aton(config->radhost, &sreq->sinr.sin_addr) != 1) {
- if ((hent = gethostbyname(config->radhost)) == NULL) {
- warn("cannot gethostbyname radhost %s", config->radhost);
- return(-2);
- }
- memcpy(&sreq->sinr.sin_addr.s_addr, hent->h_addr_list[0],
- sizeof(struct in_addr));
- }
-
- if (config->radport)
- sreq->sinr.sin_port = htons(config->radport);
- else
- sreq->sinr.sin_port = htons(PW_AUTH_UDP_PORT);
-
- /* seed the random number generator for the auth vector */
- gettimeofday(&seed, 0);
- srandom((unsigned) seed.tv_sec+seed.tv_usec);
- /* build the visible part of the auth vector randomly */
- for (i = 0; i < AUTH_VECTOR_LEN; i++)
- req.vector[i] = random() % 256;
- strlcpy((char *) secbuf, config->secret, sizeof(secbuf));
- memcpy(secbuf+strlen(config->secret), req.vector, AUTH_VECTOR_LEN);
- md5_hash(secbuf, strlen(config->secret)+AUTH_VECTOR_LEN, digest);
- /* fill in the auth_req data */
- req.code = PW_AUTHENTICATION_REQUEST;
- req.id = 0;
-
- /* bracket the username in the configured prefix/suffix */
- req.data[0] = PW_USER_NAME;
- req.data[1] = 2;
- req.data[2] = '\0';
- if (config->prefix) {
- req.data[1] += strlen(config->prefix);
- strlcat((char *) &req.data[2], config->prefix, sizeof(req.data) - 2);
- }
- req.data[1] += strlen(uname);
- strlcat((char *)&req.data[2], uname, sizeof(req.data) - 2);
- if (!strchr(uname, '@') && config->suffix) {
- req.data[1] += strlen(config->suffix);
- strlcat((char *)&req.data[2], config->suffix, sizeof(req.data) - 2);
- }
- req.datalen = req.data[1];
-
- /* set the password */
- passstart = req.datalen;
- req.data[req.datalen] = PW_PASSWORD;
- /* Null pad the password */
- passlen = (strlen(pass) + 15) / 16;
- passlen *= 16;
- req.data[req.datalen+1] = passlen+2;
- strlcpy((char *)&req.data[req.datalen+2], pass,
- sizeof(req.data) - req.datalen - 2);
- passlen -= strlen(pass);
- while (passlen--)
- req.data[req.datalen+passlen+2+strlen(pass)] = '\0';
- req.datalen += req.data[req.datalen+1];
-
- /* Add NAS_PORT and NAS_IP_ADDRESS into request */
- if ((nvalue = config->locport) == 0)
- nvalue = RADIUS_LOCAL_PORT;
- req.data[req.datalen++] = RAD_NAS_PORT;
- req.data[req.datalen++] = sizeof(nvalue) + 2;
- nvalue = htonl(nvalue);
- memcpy(req.data + req.datalen, &nvalue, sizeof(nvalue));
- req.datalen += sizeof(nvalue);
- req.data[req.datalen++] = RAD_NAS_IP_ADDRESS;
- req.data[req.datalen++] = sizeof(struct in_addr) + 2;
- memcpy(req.data + req.datalen, &sinl.sin_addr.s_addr,
- sizeof(struct in_addr));
- req.datalen += sizeof(struct in_addr);
-
- /* we're only doing authentication */
- req.data[req.datalen] = PW_SERVICE_TYPE;
- req.data[req.datalen+1] = 6;
- req.data[req.datalen+2] = (PW_SERVICE_AUTH_ONLY >> 24) & 0x000000ff;
- req.data[req.datalen+3] = (PW_SERVICE_AUTH_ONLY >> 16) & 0x000000ff;
- req.data[req.datalen+4] = (PW_SERVICE_AUTH_ONLY >> 8) & 0x000000ff;
- req.data[req.datalen+5] = PW_SERVICE_AUTH_ONLY & 0x000000ff;
- req.datalen += req.data[req.datalen+1];
-
- /* filled in the data, now we know what the actual length is. */
- req.length = 4+AUTH_VECTOR_LEN+req.datalen;
-
- /* "encrypt" the password */
- for (i = 0; i < req.data[passstart+1]-2; i += sizeof(HASH)) {
- jlen = sizeof(HASH);
- if (req.data[passstart+1]-(unsigned)i-2 < sizeof(HASH))
- jlen = req.data[passstart+1]-i-2;
- for (j = 0; j < jlen; j++)
- req.data[passstart+2+i+j] ^= digest[j];
- if (jlen == sizeof(HASH)) {
- /* Recalculate the digest from the HASHed previous */
- strlcpy((char *) secbuf, config->secret, sizeof(secbuf));
- memcpy(secbuf+strlen(config->secret), &req.data[passstart+2+i],
- sizeof(HASH));
- md5_hash(secbuf, strlen(config->secret)+sizeof(HASH), digest);
- }
- }
- sreq->reqlen = req.length;
- req.length = htons(req.length);
-
- req_copyfrom(sreq, req);
-
- /* Go to the next record in the list */
- config = config->next;
- }
-
- /* YAYY! The auth_req is ready to go! Build the reply socket and send out
- * the message. */
-
- /* now, build the sockets */
- if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- syswarn("cannot build reply socket");
- return(-1);
- }
- if (bind(sock, (struct sockaddr*) &sinl, sizeof(sinl)) < 0) {
- syswarn("cannot bind reply socket");
- close(sock);
- return(-1);
- }
-
- for(done = 0; authtries > 0 && !done; authtries--) {
- for (config = radconfig, sreq = reqtop; sreq != NULL && !done;
- config = config->next, sreq = sreq->next){
- req_copyto(req, sreq);
-
- /* send out the packet and wait for reply. */
- if (sendto(sock, (char *)&req, sreq->reqlen, 0,
- (struct sockaddr*) &sreq->sinr,
- sizeof (struct sockaddr_in)) < 0) {
- syswarn("cannot send auth_reg");
- close(sock);
- return(-1);
- }
-
- /* wait 5 seconds maximum for a radius reply. */
- now = time(0);
- end = now+5;
- tmout.tv_sec = 6;
- tmout.tv_usec = 0;
- FD_ZERO(&rdfds);
- /* store the old vector to verify next checksum */
- memcpy(secbuf+sizeof(req.vector), req.vector, sizeof(req.vector));
- FD_SET(sock, &rdfds);
- got = select(sock+1, &rdfds, 0, 0, &tmout);
- if (got < 0) {
- syswarn("cannot not select");
- break;
- } else if (got == 0) {
- /* timer ran out */
- now = time(0);
- tmout.tv_sec = end - now + 1;
- tmout.tv_usec = 0;
- continue;
- }
- slen = sizeof(sinl);
- if ((jlen = recvfrom(sock, (char *)&req, sizeof(req)-sizeof(int), 0,
- (struct sockaddr*) &sinl, &slen)) < 0) {
- syswarn("cannot recvfrom");
- break;
- }
- if (!config->ignore_source) {
- if (sinl.sin_addr.s_addr != sreq->sinr.sin_addr.s_addr ||
- (sinl.sin_port != sreq->sinr.sin_port)) {
- warn("received unexpected UDP packet from %s:%d",
- inet_ntoa(sinl.sin_addr), ntohs(sinl.sin_port));
- continue;
- }
- }
- sreq->reqlen = ntohs(req.length);
- if (jlen < 4+AUTH_VECTOR_LEN || jlen != sreq->reqlen) {
- warn("received badly-sized packet");
- continue;
- }
- /* verify the checksum */
- memcpy(((char*)&req)+sreq->reqlen, config->secret, strlen(config->secret));
- memcpy(secbuf, req.vector, sizeof(req.vector));
- memcpy(req.vector, secbuf+sizeof(req.vector), sizeof(req.vector));
- md5_hash((unsigned char *)&req, strlen(config->secret)+sreq->reqlen,
- digest);
- if (memcmp(digest, secbuf, sizeof(HASH)) != 0) {
- warn("checksum didn't match");
- continue;
- }
- /* FINALLY! Got back a known-good packet. See if we're in. */
- close(sock);
- return (req.code == PW_AUTHENTICATION_ACK) ? 0 : -1;
- done = 1;
- req_copyfrom(sreq, req);
- break;
- }
- }
- if (authtries == 0)
- warn("cannot talk to remote radius server %s:%d",
- inet_ntoa(sreq->sinr.sin_addr), ntohs(sreq->sinr.sin_port));
- return(-2);
-}
-
-#define RAD_HAVE_HOST 1
-#define RAD_HAVE_PORT 2
-#define RAD_HAVE_PREFIX 4
-#define RAD_HAVE_SUFFIX 8
-#define RAD_HAVE_LOCHOST 16
-#define RAD_HAVE_LOCPORT 32
-
-int main(int argc, char *argv[])
-{
- int opt;
- int havefile, haveother;
- struct auth_info *authinfo;
- rad_config_t radconfig;
- int retval;
- char *radius_config;
-
- message_program_name = "radius";
-
- if (!innconf_read(NULL))
- exit(1);
-
- memset(&radconfig, '\0', sizeof(rad_config_t));
- haveother = havefile = 0;
-
- while ((opt = getopt(argc, argv, "f:h")) != -1) {
- switch (opt) {
- case 'f':
- if (haveother)
- die("-f flag after another flag");
- if (!havefile) {
- /* override the standard config completely if the user
- * specifies an alternate config file */
- memset(&radconfig, '\0', sizeof(rad_config_t));
- havefile = 1;
- }
- read_config(optarg, &radconfig);
- break;
- case 'h':
- printf("Usage: radius [-f config]\n");
- exit(0);
- }
- }
- if (argc != optind)
- exit(2);
- if (!havefile) {
- radius_config = concatpath(innconf->pathetc, _PATH_RADIUS_CONFIG);
- read_config(radius_config, &radconfig);
-
- free(radius_config);
- }
-
- authinfo = get_auth_info(stdin);
- if (authinfo == NULL)
- die("failed getting auth info");
- if (authinfo->username[0] == '\0')
- die("empty username");
-
- /* got username and password, check that they're valid */
-
- retval = rad_auth(&radconfig, authinfo->username, authinfo->password);
- if (retval == -1)
- die("user %s password doesn't match", authinfo->username);
- else if (retval == -2)
- /* couldn't talk to the radius server.. output logged above. */
- exit(1);
- else if (retval != 0)
- die("unexpected return code from authentication function: %d",
- retval);
-
- /* radius password matches! */
- printf("User:%s\n", authinfo->username);
- exit(0);
-}