summaryrefslogtreecommitdiffstats
path: root/freebsd/sbin/dhclient/dhclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sbin/dhclient/dhclient.c')
-rw-r--r--freebsd/sbin/dhclient/dhclient.c179
1 files changed, 115 insertions, 64 deletions
diff --git a/freebsd/sbin/dhclient/dhclient.c b/freebsd/sbin/dhclient/dhclient.c
index 97824985..c7b75c59 100644
--- a/freebsd/sbin/dhclient/dhclient.c
+++ b/freebsd/sbin/dhclient/dhclient.c
@@ -61,6 +61,8 @@ __FBSDID("$FreeBSD$");
#include "dhcpd.h"
#include "privsep.h"
+#include <sys/capsicum.h>
+
#include <net80211/ieee80211_freebsd.h>
#ifndef _PATH_VAREMPTY
@@ -93,9 +95,10 @@ int log_perror = 1;
int privfd;
int nullfd = -1;
+char hostname[_POSIX_HOST_NAME_MAX + 1];
+
struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
-struct in_addr inaddr_any;
-struct sockaddr_in sockaddr_broadcast;
+struct in_addr inaddr_any, inaddr_broadcast;
char *path_dhclient_pidfile;
struct pidfh *pidfile;
@@ -345,6 +348,7 @@ main(int argc, char *argv[])
int immediate_daemon = 0;
struct passwd *pw;
pid_t otherpid;
+ cap_rights_t rights;
/* Initially, log errors to stderr as well as to syslogd. */
openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
@@ -389,7 +393,7 @@ main(int argc, char *argv[])
if (path_dhclient_pidfile == NULL)
error("asprintf");
}
- pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid);
+ pidfile = pidfile_open(path_dhclient_pidfile, 0644, &otherpid);
if (pidfile == NULL) {
if (errno == EEXIST)
error("dhclient already running, pid: %d.", otherpid);
@@ -412,11 +416,7 @@ main(int argc, char *argv[])
tzset();
time(&cur_time);
- memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
- sockaddr_broadcast.sin_family = AF_INET;
- sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
- sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
- sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
+ inaddr_broadcast.s_addr = INADDR_BROADCAST;
inaddr_any.s_addr = INADDR_ANY;
read_client_conf();
@@ -453,13 +453,36 @@ main(int argc, char *argv[])
error("no such user: nobody");
}
+ /*
+ * Obtain hostname before entering capability mode - it won't be
+ * possible then, as reading kern.hostname is not permitted.
+ */
+ if (gethostname(hostname, sizeof(hostname)) < 0)
+ hostname[0] = '\0';
+
+ priv_script_init("PREINIT", NULL);
+ if (ifi->client->alias)
+ priv_script_write_params("alias_", ifi->client->alias);
+ priv_script_go();
+
+ /* set up the interface */
+ discover_interfaces(ifi);
+
if (pipe(pipe_fd) == -1)
error("pipe");
fork_privchld(pipe_fd[0], pipe_fd[1]);
+ close(ifi->ufdesc);
+ ifi->ufdesc = -1;
+ close(ifi->wfdesc);
+ ifi->wfdesc = -1;
+
close(pipe_fd[0]);
privfd = pipe_fd[1];
+ cap_rights_init(&rights, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(privfd, &rights) < 0 && errno != ENOSYS)
+ error("can't limit private descriptor: %m");
if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
error("can't open and lock %s: %m", path_dhclient_db);
@@ -467,16 +490,13 @@ main(int argc, char *argv[])
rewrite_client_leases();
close(fd);
- priv_script_init("PREINIT", NULL);
- if (ifi->client->alias)
- priv_script_write_params("alias_", ifi->client->alias);
- priv_script_go();
-
if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
add_protocol("AF_ROUTE", routefd, routehandler, ifi);
-
- /* set up the interface */
- discover_interfaces(ifi);
+ if (shutdown(routefd, SHUT_WR) < 0)
+ error("can't shutdown route socket: %m");
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ);
+ if (cap_rights_limit(routefd, &rights) < 0 && errno != ENOSYS)
+ error("can't limit route socket: %m");
if (chroot(_PATH_VAREMPTY) == -1)
error("chroot");
@@ -492,6 +512,9 @@ main(int argc, char *argv[])
setproctitle("%s", ifi->name);
+ if (cap_enter() < 0 && errno != ENOSYS)
+ error("can't enter capability mode: %m");
+
if (immediate_daemon)
go_daemon();
@@ -1065,6 +1088,9 @@ packet_to_lease(struct packet *packet)
lease->address.len = sizeof(packet->raw->yiaddr);
memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
+ lease->nextserver.len = sizeof(packet->raw->siaddr);
+ memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len);
+
/* If the server name was filled out, copy it.
Do not attempt to validate the server name as a host name.
RFC 2131 merely states that sname is NUL-terminated (which do
@@ -1225,13 +1251,12 @@ again:
ip->client->secs = ip->client->packet.secs;
note("DHCPDISCOVER on %s to %s port %d interval %d",
- ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
- ntohs(sockaddr_broadcast.sin_port),
+ ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT,
(int)ip->client->interval);
/* Send out a packet. */
- (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
- inaddr_any, &sockaddr_broadcast, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, inaddr_any, inaddr_broadcast);
add_timeout(cur_time + ip->client->interval, send_discover, ip);
}
@@ -1339,8 +1364,7 @@ void
send_request(void *ipp)
{
struct interface_info *ip = ipp;
- struct sockaddr_in destination;
- struct in_addr from;
+ struct in_addr from, to;
int interval;
/* Figure out how long it's been since we started transmitting. */
@@ -1428,18 +1452,13 @@ cancel:
/* If the lease T2 time has elapsed, or if we're not yet bound,
broadcast the DHCPREQUEST rather than unicasting. */
- memset(&destination, 0, sizeof(destination));
if (ip->client->state == S_REQUESTING ||
ip->client->state == S_REBOOTING ||
cur_time > ip->client->active->rebind)
- destination.sin_addr.s_addr = INADDR_BROADCAST;
+ to.s_addr = INADDR_BROADCAST;
else
- memcpy(&destination.sin_addr.s_addr,
- ip->client->destination.iabuf,
- sizeof(destination.sin_addr.s_addr));
- destination.sin_port = htons(REMOTE_PORT);
- destination.sin_family = AF_INET;
- destination.sin_len = sizeof(destination);
+ memcpy(&to.s_addr, ip->client->destination.iabuf,
+ sizeof(to.s_addr));
if (ip->client->state != S_REQUESTING)
memcpy(&from, ip->client->active->address.iabuf,
@@ -1457,12 +1476,12 @@ cancel:
ip->client->packet.secs = htons(65535);
}
- note("DHCPREQUEST on %s to %s port %d", ip->name,
- inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
+ note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to),
+ REMOTE_PORT);
/* Send out a packet. */
- (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
- from, &destination, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, from, to);
add_timeout(cur_time + ip->client->interval, send_request, ip);
}
@@ -1473,12 +1492,11 @@ send_decline(void *ipp)
struct interface_info *ip = ipp;
note("DHCPDECLINE on %s to %s port %d", ip->name,
- inet_ntoa(sockaddr_broadcast.sin_addr),
- ntohs(sockaddr_broadcast.sin_port));
+ inet_ntoa(inaddr_broadcast), REMOTE_PORT);
/* Send out a packet. */
- (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
- inaddr_any, &sockaddr_broadcast, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, inaddr_any, inaddr_broadcast);
}
void
@@ -1535,11 +1553,10 @@ make_discover(struct interface_info *ip, struct client_lease *lease)
ip->client->config->send_options[i].len;
options[i]->timeout = 0xFFFFFFFF;
}
-
+
/* send host name if not set via config file. */
- char hostname[_POSIX_HOST_NAME_MAX+1];
if (!options[DHO_HOST_NAME]) {
- if (gethostname(hostname, sizeof(hostname)) == 0) {
+ if (hostname[0] != '\0') {
size_t len;
char* posDot = strchr(hostname, '.');
if (posDot != NULL)
@@ -1555,12 +1572,12 @@ make_discover(struct interface_info *ip, struct client_lease *lease)
}
/* set unique client identifier */
- char client_ident[sizeof(struct hardware)];
+ char client_ident[sizeof(ip->hw_address.haddr) + 1];
if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
ip->hw_address.hlen : sizeof(client_ident)-1;
client_ident[0] = ip->hw_address.htype;
- memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
+ memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
@@ -1659,11 +1676,10 @@ make_request(struct interface_info *ip, struct client_lease * lease)
ip->client->config->send_options[i].len;
options[i]->timeout = 0xFFFFFFFF;
}
-
+
/* send host name if not set via config file. */
- char hostname[_POSIX_HOST_NAME_MAX+1];
if (!options[DHO_HOST_NAME]) {
- if (gethostname(hostname, sizeof(hostname)) == 0) {
+ if (hostname[0] != '\0') {
size_t len;
char* posDot = strchr(hostname, '.');
if (posDot != NULL)
@@ -1684,7 +1700,7 @@ make_request(struct interface_info *ip, struct client_lease * lease)
int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
ip->hw_address.hlen : sizeof(client_ident)-1;
client_ident[0] = ip->hw_address.htype;
- memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
+ memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
@@ -1825,11 +1841,22 @@ void
rewrite_client_leases(void)
{
struct client_lease *lp;
+ cap_rights_t rights;
if (!leaseFile) {
leaseFile = fopen(path_dhclient_db, "w");
if (!leaseFile)
error("can't create %s: %m", path_dhclient_db);
+ cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_FSYNC,
+ CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE);
+ if (cap_rights_limit(fileno(leaseFile), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit lease descriptor: %m");
+ }
+ if (cap_fcntls_limit(fileno(leaseFile), CAP_FCNTL_GETFL) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit lease descriptor fcntls: %m");
+ }
} else {
fflush(leaseFile);
rewind(leaseFile);
@@ -1876,6 +1903,11 @@ write_client_lease(struct interface_info *ip, struct client_lease *lease,
fprintf(leaseFile, " bootp;\n");
fprintf(leaseFile, " interface \"%s\";\n", ip->name);
fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
+ if (lease->nextserver.len == sizeof(inaddr_any) &&
+ 0 != memcmp(lease->nextserver.iabuf, &inaddr_any,
+ sizeof(inaddr_any)))
+ fprintf(leaseFile, " next-server %s;\n",
+ piaddr(lease->nextserver));
if (lease->filename)
fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
if (lease->server_name)
@@ -2245,6 +2277,17 @@ script_set_env(struct client_state *client, const char *prefix,
{
int i, j, namelen;
+ /* No `` or $() command substitution allowed in environment values! */
+ for (j=0; j < strlen(value); j++)
+ switch (value[j]) {
+ case '`':
+ case '$':
+ warning("illegal character (%c) in value '%s'",
+ value[j], value);
+ /* Ignore this option */
+ return;
+ }
+
namelen = strlen(name);
for (i = 0; client->scriptEnv[i]; i++)
@@ -2281,16 +2324,6 @@ script_set_env(struct client_state *client, const char *prefix,
strlen(value) + 1);
if (client->scriptEnv[i] == NULL)
error("script_set_env: no memory for variable assignment");
-
- /* No `` or $() command substitution allowed in environment values! */
- for (j=0; j < strlen(value); j++)
- switch (value[j]) {
- case '`':
- case '$':
- error("illegal character (%c) in value '%s'", value[j],
- value);
- /* not reached */
- }
snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
}
@@ -2329,6 +2362,7 @@ void
go_daemon(void)
{
static int state = 0;
+ cap_rights_t rights;
if (no_daemon || state)
return;
@@ -2341,8 +2375,15 @@ go_daemon(void)
if (daemon(1, 0) == -1)
error("daemon");
- if (pidfile != NULL)
+ cap_rights_init(&rights);
+
+ if (pidfile != NULL) {
pidfile_write(pidfile);
+ if (cap_rights_limit(pidfile_fileno(pidfile), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit pidfile descriptor: %m");
+ }
+ }
/* we are chrooted, daemon(3) fails to open /dev/null */
if (nullfd != -1) {
@@ -2352,6 +2393,14 @@ go_daemon(void)
close(nullfd);
nullfd = -1;
}
+
+ if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS)
+ error("can't limit stdin: %m");
+ cap_rights_init(&rights, CAP_WRITE);
+ if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS)
+ error("can't limit stdout: %m");
+ if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS)
+ error("can't limit stderr: %m");
}
int
@@ -2496,19 +2545,19 @@ check_classless_option(unsigned char *data, int len)
i += 4;
continue;
} else if (width < 9) {
- addr = (in_addr_t)(data[i] << 24);
+ addr = (in_addr_t)(data[i] << 24);
i += 1;
} else if (width < 17) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16);
i += 2;
} else if (width < 25) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16) +
(in_addr_t)(data[i + 2] << 8);
i += 3;
} else if (width < 33) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16) +
(in_addr_t)(data[i + 2] << 8) +
data[i + 3];
@@ -2532,7 +2581,7 @@ check_classless_option(unsigned char *data, int len)
addr &= mask;
data[i - 1] = (unsigned char)(
(addr >> (((32 - width)/8)*8)) & 0xFF);
- }
+ }
i += 4;
}
if (i > len) {
@@ -2696,6 +2745,8 @@ fork_privchld(int fd, int fd2)
dup2(nullfd, STDERR_FILENO);
close(nullfd);
close(fd2);
+ close(ifi->rfdesc);
+ ifi->rfdesc = -1;
for (;;) {
pfd[0].fd = fd;
@@ -2707,6 +2758,6 @@ fork_privchld(int fd, int fd2)
if (nfds == 0 || !(pfd[0].revents & POLLIN))
continue;
- dispatch_imsg(fd);
+ dispatch_imsg(ifi, fd);
}
}