diff options
Diffstat (limited to 'sbin/dhclient/dhclient.c')
-rw-r--r-- | sbin/dhclient/dhclient.c | 152 |
1 files changed, 100 insertions, 52 deletions
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index f71c8c8..e16e464 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -56,9 +56,13 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/capability.h> + #include "dhcpd.h" #include "privsep.h" +#include <sys/capability.h> + #include <net80211/ieee80211_freebsd.h> #ifndef _PATH_VAREMPTY @@ -91,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; @@ -343,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); @@ -410,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(); @@ -451,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); @@ -465,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_POLL_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"); @@ -490,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(); @@ -1063,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 @@ -1223,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); } @@ -1337,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. */ @@ -1426,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, @@ -1455,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); } @@ -1471,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 @@ -1533,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) @@ -1558,7 +1577,7 @@ make_discover(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; @@ -1657,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) @@ -1682,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; @@ -1823,11 +1841,18 @@ 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_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"); + } } else { fflush(leaseFile); rewind(leaseFile); @@ -1874,6 +1899,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) @@ -2327,6 +2357,7 @@ void go_daemon(void) { static int state = 0; + cap_rights_t rights; if (no_daemon || state) return; @@ -2339,8 +2370,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) { @@ -2350,6 +2388,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 @@ -2494,19 +2540,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]; @@ -2530,7 +2576,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) { @@ -2694,6 +2740,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; @@ -2705,6 +2753,6 @@ fork_privchld(int fd, int fd2) if (nfds == 0 || !(pfd[0].revents & POLLIN)) continue; - dispatch_imsg(fd); + dispatch_imsg(ifi, fd); } } |