diff options
Diffstat (limited to 'lib/libradius/radlib.c')
-rw-r--r-- | lib/libradius/radlib.c | 737 |
1 files changed, 0 insertions, 737 deletions
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c deleted file mode 100644 index 2825c3dae786..000000000000 --- a/lib/libradius/radlib.c +++ /dev/null @@ -1,737 +0,0 @@ -/*- - * Copyright 1998 Juniper Networks, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <errno.h> -#include <md5.h> -#include <netdb.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "radlib_private.h" - -static void clear_password(struct rad_handle *); -static void generr(struct rad_handle *, const char *, ...) - __printflike(2, 3); -static void insert_scrambled_password(struct rad_handle *, int); -static int is_valid_response(struct rad_handle *, int, - const struct sockaddr_in *); -static int put_password_attr(struct rad_handle *, int, - const void *, size_t); -static int put_raw_attr(struct rad_handle *, int, - const void *, size_t); -static int split(char *, char *[], int, char *, size_t); - -static void -clear_password(struct rad_handle *h) -{ - if (h->pass_len != 0) { - memset(h->pass, 0, h->pass_len); - h->pass_len = 0; - h->pass_pos = 0; - } -} - -static void -generr(struct rad_handle *h, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vsnprintf(h->errmsg, ERRSIZE, format, ap); - va_end(ap); -} - -static void -insert_scrambled_password(struct rad_handle *h, int srv) -{ - MD5_CTX ctx; - unsigned char md5[16]; - const struct rad_server *srvp; - int padded_len; - int pos; - - srvp = &h->servers[srv]; - padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf; - - memcpy(md5, &h->request[POS_AUTH], LEN_AUTH); - for (pos = 0; pos < padded_len; pos += 16) { - int i; - - /* Calculate the new scrambler */ - MD5Init(&ctx); - MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); - MD5Update(&ctx, md5, 16); - MD5Final(md5, &ctx); - - /* - * Mix in the current chunk of the password, and copy - * the result into the right place in the request. Also - * modify the scrambler in place, since we will use this - * in calculating the scrambler for next time. - */ - for (i = 0; i < 16; i++) - h->request[h->pass_pos + pos + i] = - md5[i] ^= h->pass[pos + i]; - } -} - -/* - * Return true if the current response is valid for a request to the - * specified server. - */ -static int -is_valid_response(struct rad_handle *h, int srv, - const struct sockaddr_in *from) -{ - MD5_CTX ctx; - unsigned char md5[16]; - const struct rad_server *srvp; - int len; - - srvp = &h->servers[srv]; - - /* Check the source address */ - if (from->sin_family != srvp->addr.sin_family || - from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr || - from->sin_port != srvp->addr.sin_port) - return 0; - - /* Check the message length */ - if (h->resp_len < POS_ATTRS) - return 0; - len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; - if (len > h->resp_len) - return 0; - - /* Check the response authenticator */ - MD5Init(&ctx); - MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE); - MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH); - MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS); - MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); - MD5Final(md5, &ctx); - if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0) - return 0; - - return 1; -} - -static int -put_password_attr(struct rad_handle *h, int type, const void *value, size_t len) -{ - int padded_len; - int pad_len; - - if (h->pass_pos != 0) { - generr(h, "Multiple User-Password attributes specified"); - return -1; - } - if (len > PASSSIZE) - len = PASSSIZE; - padded_len = len == 0 ? 16 : (len+15) & ~0xf; - pad_len = padded_len - len; - - /* - * Put in a place-holder attribute containing all zeros, and - * remember where it is so we can fill it in later. - */ - clear_password(h); - put_raw_attr(h, type, h->pass, padded_len); - h->pass_pos = h->req_len - padded_len; - - /* Save the cleartext password, padded as necessary */ - memcpy(h->pass, value, len); - h->pass_len = len; - memset(h->pass + len, 0, pad_len); - return 0; -} - -static int -put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len) -{ - if (len > 253) { - generr(h, "Attribute too long"); - return -1; - } - if (h->req_len + 2 + len > MSGSIZE) { - generr(h, "Maximum message length exceeded"); - return -1; - } - h->request[h->req_len++] = type; - h->request[h->req_len++] = len + 2; - memcpy(&h->request[h->req_len], value, len); - h->req_len += len; - return 0; -} - -int -rad_add_server(struct rad_handle *h, const char *host, int port, - const char *secret, int timeout, int tries) -{ - struct rad_server *srvp; - - if (h->num_servers >= MAXSERVERS) { - generr(h, "Too many RADIUS servers specified"); - return -1; - } - srvp = &h->servers[h->num_servers]; - - memset(&srvp->addr, 0, sizeof srvp->addr); - srvp->addr.sin_len = sizeof srvp->addr; - srvp->addr.sin_family = AF_INET; - if (!inet_aton(host, &srvp->addr.sin_addr)) { - struct hostent *hent; - - if ((hent = gethostbyname(host)) == NULL) { - generr(h, "%s: host not found", host); - return -1; - } - memcpy(&srvp->addr.sin_addr, hent->h_addr, - sizeof srvp->addr.sin_addr); - } - if (port != 0) - srvp->addr.sin_port = htons(port); - else { - struct servent *sent; - - srvp->addr.sin_port = - (sent = getservbyname("radius", "udp")) != NULL ? - sent->s_port : htons(RADIUS_PORT); - } - if ((srvp->secret = strdup(secret)) == NULL) { - generr(h, "Out of memory"); - return -1; - } - srvp->timeout = timeout; - srvp->max_tries = tries; - srvp->num_tries = 0; - h->num_servers++; - return 0; -} - -void -rad_close(struct rad_handle *h) -{ - int srv; - - if (h->fd != -1) - close(h->fd); - for (srv = 0; srv < h->num_servers; srv++) { - memset(h->servers[srv].secret, 0, - strlen(h->servers[srv].secret)); - free(h->servers[srv].secret); - } - clear_password(h); - free(h); -} - -int -rad_config(struct rad_handle *h, const char *path) -{ - FILE *fp; - char buf[MAXCONFLINE]; - int linenum; - int retval; - - if (path == NULL) - path = PATH_RADIUS_CONF; - if ((fp = fopen(path, "r")) == NULL) { - generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); - return -1; - } - retval = 0; - linenum = 0; - while (fgets(buf, sizeof buf, fp) != NULL) { - int len; - char *fields[4]; - int nfields; - char msg[ERRSIZE]; - char *host; - char *port_str; - char *secret; - char *timeout_str; - char *maxtries_str; - char *end; - unsigned long timeout; - unsigned long maxtries; - int port; - - linenum++; - len = strlen(buf); - /* We know len > 0, else fgets would have returned NULL. */ - if (buf[len - 1] != '\n') { - if (len == sizeof buf - 1) - generr(h, "%s:%d: line too long", path, - linenum); - else - generr(h, "%s:%d: missing newline", path, - linenum); - retval = -1; - break; - } - buf[len - 1] = '\0'; - - /* Extract the fields from the line. */ - nfields = split(buf, fields, 4, msg, sizeof msg); - if (nfields == -1) { - generr(h, "%s:%d: %s", path, linenum, msg); - retval = -1; - break; - } - if (nfields == 0) - continue; - if (nfields < 2) { - generr(h, "%s:%d: missing shared secret", path, - linenum); - retval = -1; - break; - } - host = fields[0]; - secret = fields[1]; - timeout_str = fields[2]; - maxtries_str = fields[3]; - - /* Parse and validate the fields. */ - host = strtok(host, ":"); - port_str = strtok(NULL, ":"); - if (port_str != NULL) { - port = strtoul(port_str, &end, 10); - if (*end != '\0') { - generr(h, "%s:%d: invalid port", path, - linenum); - retval = -1; - break; - } - } else - port = 0; - if (timeout_str != NULL) { - timeout = strtoul(timeout_str, &end, 10); - if (*end != '\0') { - generr(h, "%s:%d: invalid timeout", path, - linenum); - retval = -1; - break; - } - } else - timeout = TIMEOUT; - if (maxtries_str != NULL) { - maxtries = strtoul(maxtries_str, &end, 10); - if (*end != '\0') { - generr(h, "%s:%d: invalid maxtries", path, - linenum); - retval = -1; - break; - } - } else - maxtries = MAXTRIES; - - if (rad_add_server(h, host, port, secret, timeout, maxtries) == - -1) { - char msg[ERRSIZE]; - - strcpy(msg, h->errmsg); - generr(h, "%s:%d: %s", path, linenum, msg); - retval = -1; - break; - } - } - /* Clear out the buffer to wipe a possible copy of a shared secret */ - memset(buf, 0, sizeof buf); - fclose(fp); - return retval; -} - -int -rad_create_request(struct rad_handle *h, int code) -{ - int i; - - h->request[POS_CODE] = code; - h->request[POS_IDENT] = ++h->ident; - /* Create a random authenticator */ - for (i = 0; i < LEN_AUTH; i += 2) { - long r; - r = random(); - h->request[POS_AUTH+i] = r; - h->request[POS_AUTH+i+1] = r >> 8; - } - h->req_len = POS_ATTRS; - clear_password(h); - return 0; -} - -struct in_addr -rad_cvt_addr(const void *data) -{ - struct in_addr value; - - memcpy(&value.s_addr, data, sizeof value.s_addr); - return value; -} - -u_int32_t -rad_cvt_int(const void *data) -{ - u_int32_t value; - - memcpy(&value, data, sizeof value); - return ntohl(value); -} - -char * -rad_cvt_string(const void *data, size_t len) -{ - char *s; - - s = malloc(len + 1); - if (s != NULL) { - memcpy(s, data, len); - s[len] = '\0'; - } - return s; -} - -/* - * Returns the attribute type. If none are left, returns 0. On failure, - * returns -1. - */ -int -rad_get_attr(struct rad_handle *h, const void **value, size_t *len) -{ - int type; - - if (h->resp_pos >= h->resp_len) - return 0; - if (h->resp_pos + 2 > h->resp_len) { - generr(h, "Malformed attribute in response"); - return -1; - } - type = h->response[h->resp_pos++]; - *len = h->response[h->resp_pos++] - 2; - if (h->resp_pos + *len > h->resp_len) { - generr(h, "Malformed attribute in response"); - return -1; - } - *value = &h->response[h->resp_pos]; - h->resp_pos += *len; - return type; -} - -/* - * Create and initialize a rad_handle structure, and return it to the - * caller. Can fail only if the necessary memory cannot be allocated. - * In that case, it returns NULL. - */ -struct rad_handle * -rad_open(void) -{ - struct rad_handle *h; - - h = (struct rad_handle *)malloc(sizeof(struct rad_handle)); - if (h != NULL) { - srandomdev(); - h->fd = -1; - h->num_servers = 0; - h->ident = random(); - h->errmsg[0] = '\0'; - memset(h->pass, 0, sizeof h->pass); - h->pass_len = 0; - h->pass_pos = 0; - } - return h; -} - -int -rad_put_addr(struct rad_handle *h, int type, struct in_addr addr) -{ - return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr); -} - -int -rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len) -{ - return type == RAD_USER_PASSWORD ? - put_password_attr(h, type, value, len) : - put_raw_attr(h, type, value, len); -} - -int -rad_put_int(struct rad_handle *h, int type, u_int32_t value) -{ - u_int32_t nvalue; - - nvalue = htonl(value); - return rad_put_attr(h, type, &nvalue, sizeof nvalue); -} - -int -rad_put_string(struct rad_handle *h, int type, const char *str) -{ - return rad_put_attr(h, type, str, strlen(str)); -} - -/* - * Returns the response type code on success, or -1 on failure. - */ -int -rad_send_request(struct rad_handle *h) -{ - int total_tries; - int try; - int srv; - int n; - int got_valid_response; - - /* Make sure we have a socket to use */ - if (h->fd == -1) { - struct sockaddr_in sin; - - if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - generr(h, "Cannot create socket: %s", strerror(errno)); - return -1; - } - memset(&sin, 0, sizeof sin); - sin.sin_len = sizeof sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons(0); - if (bind(h->fd, (const struct sockaddr *)&sin, - sizeof sin) == -1) { - generr(h, "bind: %s", strerror(errno)); - close(h->fd); - h->fd = -1; - return -1; - } - } - - /* Make sure the user gave us a password */ - if (h->pass_pos == 0) { - generr(h, "No User-Password attribute given"); - return -1; - } - - /* Fill in the length field in the message */ - h->request[POS_LENGTH] = h->req_len >> 8; - h->request[POS_LENGTH+1] = h->req_len; - - /* - * Count the total number of tries we will make, and zero the - * counter for each server. - */ - total_tries = 0; - for (srv = 0; srv < h->num_servers; srv++) { - total_tries += h->servers[srv].max_tries; - h->servers[srv].num_tries = 0; - } - if (total_tries == 0) { - generr(h, "No RADIUS servers specified"); - return -1; - } - - srv = 0; - got_valid_response = 0; - for (try = 0; try < total_tries; try++) { - struct timeval timelimit; - struct timeval tv; - - /* - * Scan round-robin to the next server that has some - * tries left. There is guaranteed to be one, or we - * would have exited this loop by now. - */ - while (h->servers[srv].num_tries >= - h->servers[srv].max_tries) - if (++srv >= h->num_servers) - srv = 0; - - /* Insert the scrambled password into the request */ - insert_scrambled_password(h, srv); - - /* Send the request */ - n = sendto(h->fd, h->request, h->req_len, 0, - (const struct sockaddr *)&h->servers[srv].addr, - sizeof h->servers[srv].addr); - if (n != h->req_len) { - if (n == -1) - generr(h, "sendto: %s", strerror(errno)); - else - generr(h, "sendto: short write"); - return -1; - } - h->servers[srv].num_tries++; - - /* Wait for a valid response */ - gettimeofday(&timelimit, NULL); - timelimit.tv_sec += h->servers[srv].timeout; - - tv.tv_sec = h->servers[srv].timeout; - tv.tv_usec = 0; - for ( ; ; ) { - fd_set readfds; - - FD_ZERO(&readfds); - FD_SET(h->fd, &readfds); - n = select(h->fd + 1, &readfds, NULL, NULL, &tv); - if (n == -1) { - generr(h, "select: %s", strerror(errno)); - return -1; - } - if (n == 0) /* Timed out */ - break; - if (FD_ISSET(h->fd, &readfds)) { - struct sockaddr_in from; - int fromlen; - - fromlen = sizeof from; - h->resp_len = recvfrom(h->fd, h->response, - MSGSIZE, MSG_WAITALL, - (struct sockaddr *)&from, &fromlen); - if (h->resp_len == -1) { - generr(h, "recvfrom: %s", - strerror(errno)); - return -1; - } - if (is_valid_response(h, srv, &from)) { - got_valid_response = 1; - break; - } - } - /* Compute a new timeout */ - gettimeofday(&tv, NULL); - timersub(&timelimit, &tv, &tv); - if (tv.tv_sec < 0) /* Still poll once more */ - timerclear(&tv); - } - if (got_valid_response) - break; - /* Advance to the next server */ - if (++srv >= h->num_servers) - srv = 0; - } - if (!got_valid_response) { - generr(h, "No valid RADIUS responses received"); - return -1; - } - h->resp_len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; - h->resp_pos = POS_ATTRS; - return h->response[POS_CODE]; -} - -const char * -rad_strerror(struct rad_handle *h) -{ - return h->errmsg; -} - -/* - * Destructively split a string into fields separated by white space. - * `#' at the beginning of a field begins a comment that extends to the - * end of the string. Fields may be quoted with `"'. Inside quoted - * strings, the backslash escapes `\"' and `\\' are honored. - * - * Pointers to up to the first maxfields fields are stored in the fields - * array. Missing fields get NULL pointers. - * - * The return value is the actual number of fields parsed, and is always - * <= maxfields. - * - * On a syntax error, places a message in the msg string, and returns -1. - */ -static int -split(char *str, char *fields[], int maxfields, char *msg, size_t msglen) -{ - char *p; - int i; - static const char ws[] = " \t"; - - for (i = 0; i < maxfields; i++) - fields[i] = NULL; - p = str; - i = 0; - while (*p != '\0') { - p += strspn(p, ws); - if (*p == '#' || *p == '\0') - break; - if (i >= maxfields) { - snprintf(msg, msglen, "line has too many fields"); - return -1; - } - if (*p == '"') { - char *dst; - - dst = ++p; - fields[i] = dst; - while (*p != '"') { - if (*p == '\\') { - p++; - if (*p != '"' && *p != '\\' && - *p != '\0') { - snprintf(msg, msglen, - "invalid `\\' escape"); - return -1; - } - } - if (*p == '\0') { - snprintf(msg, msglen, - "unterminated quoted string"); - return -1; - } - *dst++ = *p++; - } - *dst = '\0'; - p++; - if (*fields[i] == '\0') { - snprintf(msg, msglen, - "empty quoted string not permitted"); - return -1; - } - if (*p != '\0' && strspn(p, ws) == 0) { - snprintf(msg, msglen, "quoted string not" - " followed by white space"); - return -1; - } - } else { - fields[i] = p; - p += strcspn(p, ws); - if (*p != '\0') - *p++ = '\0'; - } - i++; - } - return i; -} |