Skip to content

Commit

Permalink
Added rlm_unpack
Browse files Browse the repository at this point in the history
Which makes it easier to unpack binary data
  • Loading branch information
alandekok committed Mar 19, 2014
1 parent 05d82cf commit 9a79b1c
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 1 deletion.
2 changes: 1 addition & 1 deletion raddb/all.mk
Expand Up @@ -11,7 +11,7 @@ DEFAULT_MODULES := always attr_filter cache_eap chap \
detail detail.log digest dhcp dynamic_clients eap \
echo exec expiration expr files linelog logintime \
mschap ntlm_auth pap passwd preprocess radutmp realm \
replicate soh sradutmp unix utf8
replicate soh sradutmp unix unpack utf8

LOCAL_MODULES := $(addprefix raddb/mods-enabled/,$(DEFAULT_MODULES))

Expand Down
38 changes: 38 additions & 0 deletions raddb/mods-available/unpack
@@ -0,0 +1,38 @@
# -*- text -*-
#
# $Id$

#
# This module is useful only for 'xlat'. To use it,
# add it to the raddb/mods-enabled/ directory. Then,
# use it on the right-hand side of a variable assignment.
#
# ... = "%{unpack:&Attribute-Name 1 integer}"
#
# The arguments are three fields:
#
# &Attribute-Name
# the name of the attribute to unpack.
# MUST be a "string" or "octets" type.
#
# 1
# The offset into the string from which
# it starts unpacking. The offset starts
# at zero, for the first attribute.
#
# integer
# the data type to unpack at that offset.
# e.g. integer, ipaddr, byte, short, etc.
#
# e.g. if we have Class = 0x00000001020304, then
#
# %{unpack:&Class 4 short}
#
# will unpack octets 4 and 5 as a "short", which has
# value 0x0304.
#
# This module is used when vendors put multiple fields
# into one attribute of type "octets".
#
unpack {
}
2 changes: 2 additions & 0 deletions src/modules/rlm_unpack/all.mk
@@ -0,0 +1,2 @@
TARGET := rlm_unpack.a
SOURCES := rlm_unpack.c
196 changes: 196 additions & 0 deletions src/modules/rlm_unpack/rlm_unpack.c
@@ -0,0 +1,196 @@
/*
* This program is is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 if the
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/

/**
* $Id$
* @file rlm_unpack.c
* @brief Unpack binary data
*
* @copyright 2014 The FreeRADIUS server project
* @copyright 2014 Alan DeKok <aland@freeradius.org>
*/
RCSID("$Id$")

#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <ctype.h>

#define PW_CAST_BASE (1850)

#define GOTO_ERROR do { RDEBUG("Unexpected text at '%s'", p); goto error;} while (0)

/** Unpack data
*
* Example: %{unpack:&Class, 1, integer}
*
* Expands Class, treating octet 1 as an "integer".
*/
static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request,
char const *fmt, char *out, size_t outlen)
{
char *data_name, *data_size, *data_type;
char *p;
size_t len;
int offset;
PW_TYPE type;
DICT_ATTR const *da;
VALUE_PAIR *vp, *cast;
char buffer[256];

strlcpy(buffer, fmt, sizeof(buffer));

p = buffer;
if (*p != '&') {
error:
RDEBUG("Format string should be like '&Class, 1, integer'");
nothing:
*out = '\0';
return 0;
}

p++;

data_name = p;
while (*p && !isspace((int) *p)) p++;
if (!*p) GOTO_ERROR;

while (isspace((int) *p)) *(p++) = '\0';
if (!*p) GOTO_ERROR;

data_size = p;

while (*p && !isspace((int) *p)) p++;
if (!*p) GOTO_ERROR;

while (isspace((int) *p)) *(p++) = '\0';
if (!*p) GOTO_ERROR;

data_type = p;

while (*p && !isspace((int) *p)) p++;
if (*p) GOTO_ERROR; /* anything after the type is an error */

if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;

if ((vp->da->type != PW_TYPE_OCTETS) &&
(vp->da->type != PW_TYPE_STRING)) {
RDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
goto nothing;
}

offset = (int) strtoul(data_size, &p, 10);
if (*p) {
RDEBUG("unpack requires a decimal number, not '%s'", data_size);
goto nothing;
}

type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
if (type == PW_TYPE_INVALID) {
RDEBUG("Invalid data type '%s'", data_type);
goto nothing;
}

/*
* Output must be a non-zero limited size.
*/
if ((dict_attr_sizes[type][0] == 0) ||
(dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
RDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
goto nothing;
}

if (vp->length < (offset + dict_attr_sizes[type][0])) {
RDEBUG("Cannot unpack attribute '%s', it is too short", data_name);
goto nothing;
}

da = dict_attrbyvalue(PW_CAST_BASE + type, 0);
if (!da) {
RDEBUG("Cannot decode type '%s'", data_type);
goto nothing;
}

cast = pairalloc(request, da);
if (!cast) goto nothing;

memcpy(&(cast->data), vp->vp_octets + offset, dict_attr_sizes[type][0]);
cast->length = dict_attr_sizes[type][0];

/*
* Hacks
*/
switch (type) {
case PW_TYPE_SIGNED:
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
cast->vp_integer = ntohl(cast->vp_integer);
break;

case PW_TYPE_SHORT:
cast->vp_short = ((vp->vp_octets[offset] << 8) |
vp->vp_octets[offset + 1]);
break;

case PW_TYPE_INTEGER64:
cast->vp_integer64 = ntohll(cast->vp_integer64);
break;

default:
break;
}

len = vp_prints_value(out, outlen, cast, 0);
talloc_free(cast);

return len;
}


/*
* Register the xlats
*/
static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
{
xlat_register("unpack", unpack_xlat, NULL, instance);

return 0;
}

/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
*
* If the module needs to temporarily modify it's instantiation
* data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
* The server will then take care of ensuring that the module
* is single-threaded.
*/
module_t rlm_unpack = {
RLM_MODULE_INIT,
"unpack",
RLM_TYPE_THREAD_SAFE, /* type */
0,
NULL,
mod_instantiate, /* instantiation */
NULL, /* detach */
{
NULL, /* authentication */
NULL, /* authorization */
NULL, NULL, NULL,
NULL, /* pre-proxy */
NULL, /* post-proxy */
NULL /* post-auth */
},
};

0 comments on commit 9a79b1c

Please sign in to comment.