Do more boundary and sanity checking when adding dhcp options.

Simplify parameter list for ifchd_cmd().
This commit is contained in:
Nicholas J. Kain 2011-03-30 15:57:01 -04:00
parent 0e62d46f8b
commit ddf9daf8e4
5 changed files with 143 additions and 91 deletions

View File

@ -79,7 +79,8 @@ static void init_header(struct dhcpMessage *packet, char type)
packet->hlen = 6; // ETH_10MB_LEN packet->hlen = 6; // ETH_10MB_LEN
packet->cookie = htonl(DHCP_MAGIC); packet->cookie = htonl(DHCP_MAGIC);
packet->options[0] = DHCP_END; packet->options[0] = DHCP_END;
add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); add_u32_option(packet->options, DHCP_OPTIONS_BUFSIZE, DHCP_MESSAGE_TYPE,
type);
} }
/* initialize a packet with the proper defaults */ /* initialize a packet with the proper defaults */
@ -93,10 +94,13 @@ static void init_packet(struct dhcpMessage *packet, char type)
init_header(packet, type); init_header(packet, type);
memcpy(packet->chaddr, client_config.arp, 6); memcpy(packet->chaddr, client_config.arp, 6);
add_option_string(packet->options, client_config.clientid); add_option_string(packet->options, DHCP_OPTIONS_BUFSIZE,
client_config.clientid);
if (client_config.hostname) if (client_config.hostname)
add_option_string(packet->options, client_config.hostname); add_option_string(packet->options, DHCP_OPTIONS_BUFSIZE,
add_option_string(packet->options, (unsigned char *)&vendor_id); client_config.hostname);
add_option_string(packet->options, DHCP_OPTIONS_BUFSIZE,
(unsigned char *)&vendor_id);
} }
#define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" #define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff"
@ -117,11 +121,13 @@ int send_discover(uint32_t xid, uint32_t requested)
init_packet(&packet, DHCPDISCOVER); init_packet(&packet, DHCPDISCOVER);
packet.xid = xid; packet.xid = xid;
if (requested) if (requested)
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_REQUESTED_IP,
requested);
/* Request a RFC-specified max size to work around buggy servers. */ /* Request a RFC-specified max size to work around buggy servers. */
add_simple_option(packet.options, DHCP_MAX_SIZE, htons(576)); add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE,
add_requests(&packet); DHCP_MAX_SIZE, htons(576));
add_option_request_list(&packet);
log_line("Sending discover..."); log_line("Sending discover...");
return bcast_raw_packet(&packet); return bcast_raw_packet(&packet);
} }
@ -135,10 +141,11 @@ int send_selecting(uint32_t xid, uint32_t server, uint32_t requested)
init_packet(&packet, DHCPREQUEST); init_packet(&packet, DHCPREQUEST);
packet.xid = xid; packet.xid = xid;
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_REQUESTED_IP,
add_simple_option(packet.options, DHCP_SERVER_ID, server); requested);
add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_SERVER_ID, server);
add_requests(&packet); add_option_request_list(&packet);
addr.s_addr = requested; addr.s_addr = requested;
log_line("Sending select for %s...", inet_ntoa(addr)); log_line("Sending select for %s...", inet_ntoa(addr));
return bcast_raw_packet(&packet); return bcast_raw_packet(&packet);
@ -153,7 +160,7 @@ int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
packet.xid = xid; packet.xid = xid;
packet.ciaddr = ciaddr; packet.ciaddr = ciaddr;
add_requests(&packet); add_option_request_list(&packet);
log_line("Sending renew..."); log_line("Sending renew...");
if (server) if (server)
return kernel_packet(&packet, ciaddr, DHCP_CLIENT_PORT, server, return kernel_packet(&packet, ciaddr, DHCP_CLIENT_PORT, server,
@ -179,8 +186,9 @@ int send_decline(uint32_t xid, uint32_t server, uint32_t requested)
*/ */
packet.xid = xid; packet.xid = xid;
/* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */ /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_REQUESTED_IP,
add_simple_option(packet.options, DHCP_SERVER_ID, server); requested);
add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_SERVER_ID, server);
log_line("Sending decline..."); log_line("Sending decline...");
return bcast_raw_packet(&packet); return bcast_raw_packet(&packet);
@ -195,8 +203,9 @@ int send_release(uint32_t server, uint32_t ciaddr)
packet.xid = random_xid(); packet.xid = random_xid();
packet.ciaddr = ciaddr; packet.ciaddr = ciaddr;
add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_REQUESTED_IP,
add_simple_option(packet.options, DHCP_SERVER_ID, server); ciaddr);
add_u32_option(packet.options, DHCP_OPTIONS_BUFSIZE, DHCP_SERVER_ID, server);
log_line("Sending release..."); log_line("Sending release...");
return kernel_packet(&packet, ciaddr, DHCP_CLIENT_PORT, server, return kernel_packet(&packet, ciaddr, DHCP_CLIENT_PORT, server,

View File

@ -1,5 +1,5 @@
/* ifchange.c - functions to call the interface change daemon /* ifchange.c - functions to call the interface change daemon
* Time-stamp: <2011-03-30 12:02:55 nk> * Time-stamp: <2011-03-30 13:14:37 nk>
* *
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com> * (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
* *
@ -40,13 +40,14 @@
/* Fill buf with the ifchd command text of option 'option'. */ /* Fill buf with the ifchd command text of option 'option'. */
/* Returns 0 if successful, -1 if nothing was filled in. */ /* Returns 0 if successful, -1 if nothing was filled in. */
static int ifchd_cmd(char *buf, size_t buflen, static int ifchd_cmd(char *buf, size_t buflen, uint8_t *option, ssize_t optlen,
unsigned char *option, ssize_t optlen, uint8_t code)
uint8_t code, enum option_type type, const char *optname)
{ {
char *obuf = buf; char *obuf = buf;
uint8_t *ooption = option; uint8_t *ooption = option;
ssize_t typelen = option_length(type); const char *optname = option_name(code);
enum option_type type = option_type(code);
ssize_t typelen = option_length(code);
if (!option || type == OPTION_NONE) if (!option || type == OPTION_NONE)
return -1; return -1;
@ -181,8 +182,7 @@ static void send_cmd(int sockfd, struct dhcpMessage *packet,
memset(buf, '\0', sizeof buf); memset(buf, '\0', sizeof buf);
optdata = get_option_data(packet, code, &optlen); optdata = get_option_data(packet, code, &optlen);
if (ifchd_cmd(buf, sizeof buf, optdata, optlen, code, option_type(code), if (ifchd_cmd(buf, sizeof buf, optdata, optlen, code) == -1)
option_name(code)) == -1)
return; return;
sockwrite(sockfd, buf, strlen(buf)); sockwrite(sockfd, buf, strlen(buf));
} }

View File

@ -1,7 +1,21 @@
/* /* options.c - DHCP options handling
* options.c -- DHCP server option packet tools * Time-stamp: <2011-03-30 15:52:12 nk>
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 *
* Fixes and hardening: Nicholas J. Kain <njkain at gmail dot com> * (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <stdio.h> #include <stdio.h>
@ -13,12 +27,6 @@
#include "log.h" #include "log.h"
#include "malloc.h" #include "malloc.h"
enum {
OPT_CODE = 0,
OPT_LEN = 1,
OPT_DATA = 2
};
struct dhcp_option { struct dhcp_option {
char name[10]; char name[10];
enum option_type type; enum option_type type;
@ -55,7 +63,7 @@ static struct dhcp_option options[] = {
{"maxsize" , OPTION_U16, 0x39}, {"maxsize" , OPTION_U16, 0x39},
{"tftp" , OPTION_STRING, 0x42}, {"tftp" , OPTION_STRING, 0x42},
{"bootfile" , OPTION_STRING, 0x43}, {"bootfile" , OPTION_STRING, 0x43},
{"" , OPTION_NONE, 0x00} {"NONE" , OPTION_NONE, 0x00}
}; };
// List of options that will be sent on the parameter request list to the // List of options that will be sent on the parameter request list to the
@ -102,7 +110,7 @@ const char *option_name(uint8_t code)
return bad_option_name; return bad_option_name;
} }
uint8_t option_length(enum option_type type) static uint8_t option_type_length(enum option_type type)
{ {
switch (type) { switch (type) {
case OPTION_IP: return 4; case OPTION_IP: return 4;
@ -115,6 +123,16 @@ uint8_t option_length(enum option_type type)
} }
} }
uint8_t option_length(uint8_t code)
{
int i;
for (i = 0; options[i].code; i++)
if (options[i].code == code)
return option_type_length(options[i].type);
log_warning("option_length: unknown length for code 0x%02x", code);
return 0;
}
int option_valid_list(uint8_t code) int option_valid_list(uint8_t code)
{ {
int i; int i;
@ -254,61 +272,60 @@ uint8_t *get_option_data(struct dhcpMessage *packet, int code, ssize_t *optlen)
} }
/* return the position of the 'end' option */ /* return the position of the 'end' option */
int get_end_option_idx(uint8_t *optbuf, size_t bufsize) ssize_t get_end_option_idx(uint8_t *optbuf, size_t bufsize)
{ {
int i = 0; int i;
while (i < bufsize && optbuf[i] != DHCP_END) { for (i = 0; i < bufsize; ++i) {
if (optbuf[i] != DHCP_PADDING) if (optbuf[i] == DHCP_END)
i += optbuf[i+1] + 2;
else
i++;
}
if (i < bufsize - 1)
return i; return i;
else { if (optbuf[i] == DHCP_PADDING)
log_warning("get_end_option_idx(): did not find DHCP_END marker"); continue;
return bufsize - 1; if (optbuf[i] != DHCP_PADDING)
i += optbuf[i+1] + 1;
} }
log_warning("get_end_option_idx(): did not find DHCP_END marker");
return -1;
} }
/* add an option string to the options (an option string contains an option /* add an option string to the options (an option string contains an option
* code, length, then data) */ * code, length, then data) */
int add_option_string(unsigned char *optionptr, unsigned char *string) size_t add_option_string(uint8_t *optbuf, size_t buflen, uint8_t *optstr)
{ {
// XXX: not real length size_t end = get_end_option_idx(optbuf, buflen);
int end = get_end_option_idx(optionptr, DHCP_OPTIONS_BUFSIZE); size_t datalen = optstr[1];
/* end position + string length + option code/length + end option */ if (end == -1) {
if (end + string[OPT_LEN] + 2 + 1 >= DHCP_OPTIONS_BUFSIZE) { log_warning("add_option_string: Buffer has no DHCP_END marker");
log_error("Option 0x%02x did not fit into the packet!",
string[OPT_CODE]);
return 0; return 0;
} }
memcpy(optionptr + end, string, string[OPT_LEN] + 2); /* end position + optstr length + option code/length + end option */
optionptr[end + string[OPT_LEN] + 2] = DHCP_END; if (end + datalen + 2 + 1 >= buflen) {
return string[OPT_LEN] + 2; log_warning("Option 0x%02x did not fit into the packet!", optstr[0]);
return 0;
}
memcpy(optbuf + end, optstr, datalen + 2);
optbuf[end + datalen + 2] = DHCP_END;
return datalen + 2;
} }
int add_simple_option(unsigned char *optionptr, unsigned char code, size_t add_u32_option(uint8_t *optbuf, size_t buflen, uint8_t code,
uint32_t data) uint32_t data)
{ {
int i, length = 0; int length = 0;
unsigned char option[2 + 4]; uint8_t option[6];
for (i = 0; options[i].code; i++) length = option_length(code);
if (options[i].code == code) {
length = option_length(options[i].type);
}
log_line("aso(): code=0x%02x length=0x%02x", code, length); log_line("add_u32_option: code=0x%02x length=0x%02x", code, length);
option[OPT_CODE] = code; option[0] = code;
option[OPT_LEN] = (unsigned char)length; option[1] = length;
if (!length) { if (!length) {
log_error("Could not add option 0x%02x", code); log_warning("add_u32_option: option code 0x%02x has 0 length", code);
return 0; return 0;
} else if (length == 1) { }
if (length == 1) {
uint8_t t = (uint8_t)data; uint8_t t = (uint8_t)data;
memcpy(option + 2, &t, 1); memcpy(option + 2, &t, 1);
} else if (length == 2) { } else if (length == 2) {
@ -318,20 +335,18 @@ int add_simple_option(unsigned char *optionptr, unsigned char code,
uint32_t t = (uint32_t)data; uint32_t t = (uint32_t)data;
memcpy(option + 2, &t, 4); memcpy(option + 2, &t, 4);
} }
return add_option_string(optionptr, option); return add_option_string(optbuf, buflen, option);
} }
/* Add a paramater request list for stubborn DHCP servers. Don't do bounds */ /* Add a paramater request list for stubborn DHCP servers */
/* checking here because it goes towards the head of the packet. */ void add_option_request_list(struct dhcpMessage *packet)
void add_requests(struct dhcpMessage *packet)
{ {
// XXX: not real length int i;
int end = get_end_option_idx(packet->options, DHCP_OPTIONS_BUFSIZE); uint8_t reqdata[sizeof req_opts + 1];
int i, len = 0;
packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; reqdata[0] = DHCP_PARAM_REQ;
reqdata[1] = sizeof reqdata - 2;
for (i = 0; req_opts[i]; i++) for (i = 0; req_opts[i]; i++)
packet->options[end + OPT_DATA + len++] = req_opts[i]; reqdata[i + 2] = req_opts[i];
packet->options[end + OPT_LEN] = len; add_option_string(packet->options, DHCP_OPTIONS_BUFSIZE, reqdata);
packet->options[end + OPT_DATA + len] = DHCP_END;
} }

View File

@ -1,4 +1,22 @@
/* options.h */ /* options.h - DHCP options handling
* Time-stamp: <2011-03-30 15:50:30 nk>
*
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef OPTIONS_H_ #ifndef OPTIONS_H_
#define OPTIONS_H_ #define OPTIONS_H_
@ -64,7 +82,7 @@ enum option_type {
const char *option_name(uint8_t code); const char *option_name(uint8_t code);
enum option_type option_type(uint8_t code); enum option_type option_type(uint8_t code);
uint8_t option_length(enum option_type type); uint8_t option_length(uint8_t code);
int option_valid_list(uint8_t code); int option_valid_list(uint8_t code);
size_t sizeof_option(unsigned char code, size_t datalen); size_t sizeof_option(unsigned char code, size_t datalen);
size_t set_option(unsigned char *buf, size_t buflen, unsigned char code, size_t set_option(unsigned char *buf, size_t buflen, unsigned char code,
@ -76,10 +94,10 @@ unsigned char *alloc_dhcp_client_id_option(unsigned char type,
unsigned char *idstr, size_t idstrlen); unsigned char *idstr, size_t idstrlen);
uint8_t *get_option_data(struct dhcpMessage *packet, int code, ssize_t *optlen); uint8_t *get_option_data(struct dhcpMessage *packet, int code, ssize_t *optlen);
int get_end_option_idx(uint8_t *optbuf, size_t bufsize); ssize_t get_end_option_idx(uint8_t *optbuf, size_t bufsize);
int add_option_string(unsigned char *optionptr, unsigned char *string); size_t add_option_string(uint8_t *optbuf, size_t buflen, uint8_t *optstr);
int add_simple_option(unsigned char *optionptr, unsigned char code, uint32_t data); size_t add_u32_option(uint8_t *optbuf, size_t buflen, uint8_t code,
uint32_t data);
void add_requests(struct dhcpMessage *packet); void add_option_request_list(struct dhcpMessage *packet);
#endif #endif

View File

@ -105,8 +105,13 @@ int raw_packet(struct dhcpMessage *payload, uint32_t source_ip,
* In order to work with those buggy servers, * In order to work with those buggy servers,
* we truncate packets after end option byte. * we truncate packets after end option byte.
*/ */
padding = DHCP_OPTIONS_BUFSIZE - 1 - get_end_option_idx(packet.data.options, ssize_t endloc = get_end_option_idx(packet.data.options,
DHCP_OPTIONS_BUFSIZE); DHCP_OPTIONS_BUFSIZE);
if (endloc == -1) {
log_error("raw_packet: attempt to send packet with no DHCP_END");
goto out_fd;
}
padding = DHCP_OPTIONS_BUFSIZE - 1 - endloc;
packet.ip.protocol = IPPROTO_UDP; packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = source_ip; packet.ip.saddr = source_ip;
@ -165,8 +170,13 @@ int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip,
if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
goto out_fd; goto out_fd;
padding = DHCP_OPTIONS_BUFSIZE - 1 - get_end_option_idx(payload->options, ssize_t endloc = get_end_option_idx(payload->options,
DHCP_OPTIONS_BUFSIZE); DHCP_OPTIONS_BUFSIZE);
if (endloc == -1) {
log_error("kernel_packet: attempt to send packet with no DHCP_END");
goto out_fd;
}
padding = DHCP_OPTIONS_BUFSIZE - 1 - endloc;
result = safe_write(fd, (const char *)payload, DHCP_SIZE - padding); result = safe_write(fd, (const char *)payload, DHCP_SIZE - padding);
if (result == -1) if (result == -1)
log_error("kernel_packet: write failed: %s", strerror(errno)); log_error("kernel_packet: write failed: %s", strerror(errno));