Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/rtsold/rtsol.c
Show All 29 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * 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 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/capsicum.h> | |||||
#include <sys/queue.h> | |||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/stat.h> | |||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/queue.h> | |||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <sys/stat.h> | |||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ | #define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#undef __BSD_VISIBLE | #undef __BSD_VISIBLE | ||||
Show All 11 Lines | |||||
#include <time.h> | #include <time.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <syslog.h> | #include <syslog.h> | ||||
#include "rtsold.h" | #include "rtsold.h" | ||||
static struct msghdr rcvmhdr; | |||||
static struct msghdr sndmhdr; | |||||
static struct iovec rcviov[2]; | |||||
static struct iovec sndiov[2]; | |||||
static struct sockaddr_in6 from; | |||||
static int rcvcmsglen; | |||||
int rssock; | |||||
static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST]; | static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST]; | ||||
struct ifinfo_head_t ifinfo_head = | struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head); | ||||
TAILQ_HEAD_INITIALIZER(ifinfo_head); | |||||
static const struct sockaddr_in6 sin6_allrouters = { | static const struct sockaddr_in6 sin6_allrouters = { | ||||
.sin6_len = sizeof(sin6_allrouters), | .sin6_len = sizeof(sin6_allrouters), | ||||
.sin6_family = AF_INET6, | .sin6_family = AF_INET6, | ||||
.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, | .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, | ||||
}; | }; | ||||
static void call_script(const int, const char *const *, | static void call_script(const char *const *, struct script_msg_head_t *); | ||||
struct script_msg_head_t *); | |||||
static size_t dname_labeldec(char *, size_t, const char *); | static size_t dname_labeldec(char *, size_t, const char *); | ||||
static int safefile(const char *); | |||||
static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); | static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); | ||||
static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, | static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, | ||||
struct script_msg_head_t *, struct script_msg_head_t *); | struct script_msg_head_t *, struct script_msg_head_t *); | ||||
static char *make_rsid(const char *, const char *, struct rainfo *); | static char *make_rsid(const char *, const char *, struct rainfo *); | ||||
#define _ARGS_OTHER otherconf_script, ifi->ifname | #define _ARGS_OTHER otherconf_script, ifi->ifname | ||||
#define _ARGS_RESADD resolvconf_script, "-a", rsid | #define _ARGS_RESADD resolvconf_script, "-a", rsid | ||||
#define _ARGS_RESDEL resolvconf_script, "-d", rsid | #define _ARGS_RESDEL resolvconf_script, "-d", rsid | ||||
#define CALL_SCRIPT(name, sm_head) do { \ | #define CALL_SCRIPT(name, sm_head) do { \ | ||||
const char *const sarg[] = { _ARGS_##name, NULL }; \ | const char *const sarg[] = { _ARGS_##name, NULL }; \ | ||||
call_script(sizeof(sarg), sarg, sm_head); \ | call_script(sarg, sm_head); \ | ||||
} while (0) | } while (0) | ||||
#define ELM_MALLOC(p, error_action) do { \ | #define ELM_MALLOC(p, error_action) do { \ | ||||
p = malloc(sizeof(*p)); \ | p = malloc(sizeof(*p)); \ | ||||
if (p == NULL) { \ | if (p == NULL) { \ | ||||
warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ | warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ | ||||
strerror(errno)); \ | strerror(errno)); \ | ||||
error_action; \ | error_action; \ | ||||
} \ | } \ | ||||
memset(p, 0, sizeof(*p)); \ | memset(p, 0, sizeof(*p)); \ | ||||
} while (0) | } while (0) | ||||
int | int | ||||
sockopen(void) | recvsockopen(void) | ||||
{ | { | ||||
static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; | |||||
int sndcmsglen, on; | |||||
static u_char answer[1500]; | |||||
struct icmp6_filter filt; | struct icmp6_filter filt; | ||||
cap_rights_t rights; | |||||
int on, sock; | |||||
sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + | if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { | ||||
CMSG_SPACE(sizeof(int)); | |||||
if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"malloc for receive msghdr failed"); | |||||
return (-1); | |||||
} | |||||
if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"malloc for send msghdr failed"); | |||||
return (-1); | |||||
} | |||||
if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); | warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); | ||||
return (-1); | goto fail; | ||||
} | } | ||||
/* specify to tell receiving interface */ | /* Provide info about the receiving interface. */ | ||||
on = 1; | on = 1; | ||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, | if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, | ||||
sizeof(on)) < 0) { | sizeof(on)) < 0) { | ||||
warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", | warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVPKTINFO): %s", | ||||
strerror(errno)); | strerror(errno)); | ||||
exit(1); | goto fail; | ||||
} | } | ||||
/* specify to tell value of hoplimit field of received IP6 hdr */ | /* Include the hop limit from the received header. */ | ||||
on = 1; | on = 1; | ||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, | if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, | ||||
sizeof(on)) < 0) { | sizeof(on)) < 0) { | ||||
warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", | warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVHOPLIMIT): %s", | ||||
strerror(errno)); | strerror(errno)); | ||||
exit(1); | goto fail; | ||||
} | } | ||||
/* specfiy to accept only router advertisements on the socket */ | /* Filter out everything except for Router Advertisements. */ | ||||
ICMP6_FILTER_SETBLOCKALL(&filt); | ICMP6_FILTER_SETBLOCKALL(&filt); | ||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); | ||||
if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, | if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, | ||||
sizeof(filt)) == -1) { | sizeof(filt)) == -1) { | ||||
warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", | warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", | ||||
strerror(errno)); | strerror(errno)); | ||||
goto fail; | |||||
} | |||||
(void)cap_rights_init(&rights, CAP_EVENT, CAP_RECV); | |||||
if (cap_rights_limit(sock, &rights) < 0) { | |||||
oshogbo: errno or caph_rights_limit. | |||||
warnmsg(LOG_ERR, __func__, "cap_rights_limit(): %s", | |||||
strerror(errno)); | |||||
goto fail; | |||||
} | |||||
return (sock); | |||||
fail: | |||||
if (sock >= 0) | |||||
(void)close(sock); | |||||
return(-1); | return (-1); | ||||
} | } | ||||
/* initialize msghdr for receiving packets */ | int | ||||
rcviov[0].iov_base = (caddr_t)answer; | sendsockopen(uint32_t linkid) | ||||
rcviov[0].iov_len = sizeof(answer); | { | ||||
rcvmhdr.msg_name = (caddr_t)&from; | struct icmp6_filter filt; | ||||
rcvmhdr.msg_iov = rcviov; | struct sockaddr_in6 dst; | ||||
rcvmhdr.msg_iovlen = 1; | cap_rights_t rights; | ||||
rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; | int sock; | ||||
/* initialize msghdr for sending packets */ | if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { | ||||
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); | warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); | ||||
sndmhdr.msg_iov = sndiov; | goto fail; | ||||
sndmhdr.msg_iovlen = 1; | } | ||||
sndmhdr.msg_control = (caddr_t)sndcmsgbuf; | |||||
sndmhdr.msg_controllen = sndcmsglen; | |||||
return (rssock); | /* | ||||
* We don't want to receive data on send sockets. | |||||
*/ | |||||
ICMP6_FILTER_SETBLOCKALL(&filt); | |||||
if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, | |||||
sizeof(filt)) == -1) { | |||||
warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", | |||||
strerror(errno)); | |||||
goto fail; | |||||
} | } | ||||
/* | |||||
* Explicitly set the destination address now so that we can send on | |||||
* this socket in capability mode. | |||||
*/ | |||||
dst = sin6_allrouters; | |||||
dst.sin6_scope_id = linkid; | |||||
if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "connect(): %s", strerror(errno)); | |||||
goto fail; | |||||
} | |||||
if (cap_rights_limit(sock, cap_rights_init(&rights, CAP_SEND)) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "cap_rights_limit(): %s", | |||||
strerror(errno)); | |||||
goto fail; | |||||
} | |||||
Done Inline Actionserrno or caph. oshogbo: errno or caph. | |||||
return (sock); | |||||
fail: | |||||
if (sock >= 0) | |||||
(void)close(sock); | |||||
Not Done Inline ActionsAre you interested in keeping errno? oshogbo: Are you interested in keeping errno? | |||||
Done Inline ActionsNo, we already printed the warning. markj: No, we already printed the warning. | |||||
return (-1); | |||||
} | |||||
void | void | ||||
sendpacket(struct ifinfo *ifi) | sendpacket(struct ifinfo *ifi) | ||||
{ | { | ||||
uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + | |||||
CMSG_SPACE(sizeof(int))]; | |||||
struct iovec iov; | |||||
struct msghdr hdr; | |||||
struct in6_pktinfo *pi; | struct in6_pktinfo *pi; | ||||
struct cmsghdr *cm; | struct cmsghdr *cm; | ||||
int hoplimit = 255; | int hoplimit = 255; | ||||
ssize_t i; | ssize_t n; | ||||
struct sockaddr_in6 dst; | |||||
dst = sin6_allrouters; | memset(&hdr, 0, sizeof(hdr)); | ||||
dst.sin6_scope_id = ifi->linkid; | hdr.msg_iov = &iov; | ||||
hdr.msg_iovlen = 1; | |||||
hdr.msg_control = cmsg; | |||||
hdr.msg_controllen = sizeof(cmsg); | |||||
sndmhdr.msg_name = (caddr_t)&dst; | iov.iov_base = (caddr_t)ifi->rs_data; | ||||
sndmhdr.msg_iov[0].iov_base = (caddr_t)ifi->rs_data; | iov.iov_len = ifi->rs_datalen; | ||||
sndmhdr.msg_iov[0].iov_len = ifi->rs_datalen; | |||||
cm = CMSG_FIRSTHDR(&sndmhdr); | /* Specify the outbound interface. */ | ||||
/* specify the outgoing interface */ | cm = CMSG_FIRSTHDR(&hdr); | ||||
cm->cmsg_level = IPPROTO_IPV6; | cm->cmsg_level = IPPROTO_IPV6; | ||||
cm->cmsg_type = IPV6_PKTINFO; | cm->cmsg_type = IPV6_PKTINFO; | ||||
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | ||||
pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); | pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); | ||||
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ | memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ | ||||
pi->ipi6_ifindex = ifi->sdl->sdl_index; | pi->ipi6_ifindex = ifi->sdl->sdl_index; | ||||
/* specify the hop limit of the packet */ | /* Specify the packet's hop limit. */ | ||||
cm = CMSG_NXTHDR(&sndmhdr, cm); | cm = CMSG_NXTHDR(&hdr, cm); | ||||
cm->cmsg_level = IPPROTO_IPV6; | cm->cmsg_level = IPPROTO_IPV6; | ||||
cm->cmsg_type = IPV6_HOPLIMIT; | cm->cmsg_type = IPV6_HOPLIMIT; | ||||
cm->cmsg_len = CMSG_LEN(sizeof(int)); | cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); | ||||
memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); | ||||
warnmsg(LOG_DEBUG, __func__, | warnmsg(LOG_DEBUG, __func__, "sent RS on %s, state is %d", | ||||
"send RS on %s, whose state is %d", | |||||
ifi->ifname, ifi->state); | ifi->ifname, ifi->state); | ||||
i = sendmsg(rssock, &sndmhdr, 0); | n = sendmsg(ifi->ifi_sock, &hdr, 0); | ||||
if (i < 0 || (size_t)i != ifi->rs_datalen) { | if (n < 0 || (size_t)n != ifi->rs_datalen) { | ||||
/* | /* | ||||
* ENETDOWN is not so serious, especially when using several | * ENETDOWN is not so serious, especially when using several | ||||
* network cards on a mobile node. We ignore it. | * network cards on a mobile node. We ignore it. | ||||
*/ | */ | ||||
if (errno != ENETDOWN || dflag > 0) | if (errno != ENETDOWN || dflag > 0) | ||||
warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", | warnmsg(LOG_ERR, __func__, "sendmsg on %s %zd: %s", | ||||
ifi->ifname, strerror(errno)); | ifi->ifname, n, strerror(errno)); | ||||
} | } | ||||
/* update counter */ | |||||
ifi->probes++; | ifi->probes++; | ||||
} | } | ||||
void | void | ||||
rtsol_input(int s) | rtsol_input(int sock) | ||||
{ | { | ||||
char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; | uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + | ||||
CMSG_SPACE(sizeof(int))]; | |||||
struct iovec iov; | |||||
struct msghdr hdr; | |||||
struct sockaddr_in6 from; | |||||
char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; | |||||
int l, ifindex = 0, *hlimp = NULL; | int l, ifindex = 0, *hlimp = NULL; | ||||
ssize_t msglen; | ssize_t msglen; | ||||
struct in6_pktinfo *pi = NULL; | struct in6_pktinfo *pi = NULL; | ||||
struct ifinfo *ifi = NULL; | struct ifinfo *ifi = NULL; | ||||
struct ra_opt *rao = NULL; | struct ra_opt *rao = NULL; | ||||
struct icmp6_hdr *icp; | struct icmp6_hdr *icp; | ||||
struct nd_router_advert *nd_ra; | struct nd_router_advert *nd_ra; | ||||
struct cmsghdr *cm; | struct cmsghdr *cm; | ||||
struct rainfo *rai; | struct rainfo *rai; | ||||
char *raoptp; | char *p, *raoptp; | ||||
char *p; | |||||
struct in6_addr *addr; | struct in6_addr *addr; | ||||
struct nd_opt_hdr *ndo; | struct nd_opt_hdr *ndo; | ||||
struct nd_opt_rdnss *rdnss; | struct nd_opt_rdnss *rdnss; | ||||
struct nd_opt_dnssl *dnssl; | struct nd_opt_dnssl *dnssl; | ||||
size_t len; | size_t len; | ||||
char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; | char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; | ||||
char dname[NI_MAXHOST]; | char dname[NI_MAXHOST]; | ||||
struct timespec now; | struct timespec lifetime, now; | ||||
struct timespec lifetime; | int newent_rai, newent_rao; | ||||
int newent_rai; | |||||
int newent_rao; | |||||
/* get message. namelen and controllen must always be initialized. */ | memset(&hdr, 0, sizeof(hdr)); | ||||
rcvmhdr.msg_namelen = sizeof(from); | hdr.msg_iov = &iov; | ||||
rcvmhdr.msg_controllen = rcvcmsglen; | hdr.msg_iovlen = 1; | ||||
if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { | hdr.msg_name = &from; | ||||
hdr.msg_namelen = sizeof(from); | |||||
hdr.msg_control = cmsg; | |||||
hdr.msg_controllen = sizeof(cmsg); | |||||
iov.iov_base = (caddr_t)answer; | |||||
iov.iov_len = sizeof(answer); | |||||
if ((msglen = recvmsg(sock, &hdr, 0)) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); | warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); | ||||
return; | return; | ||||
} | } | ||||
/* extract optional information via Advanced API */ | /* Extract control message info. */ | ||||
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; | for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL; | ||||
cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { | cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) { | ||||
if (cm->cmsg_level == IPPROTO_IPV6 && | if (cm->cmsg_level == IPPROTO_IPV6 && | ||||
cm->cmsg_type == IPV6_PKTINFO && | cm->cmsg_type == IPV6_PKTINFO && | ||||
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { | cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { | ||||
pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm)); | pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm)); | ||||
ifindex = pi->ipi6_ifindex; | ifindex = pi->ipi6_ifindex; | ||||
} | } | ||||
if (cm->cmsg_level == IPPROTO_IPV6 && | if (cm->cmsg_level == IPPROTO_IPV6 && | ||||
cm->cmsg_type == IPV6_HOPLIMIT && | cm->cmsg_type == IPV6_HOPLIMIT && | ||||
Show All 13 Lines | rtsol_input(int sock) | ||||
} | } | ||||
if ((size_t)msglen < sizeof(struct nd_router_advert)) { | if ((size_t)msglen < sizeof(struct nd_router_advert)) { | ||||
warnmsg(LOG_INFO, __func__, | warnmsg(LOG_INFO, __func__, | ||||
"packet size(%zd) is too short", msglen); | "packet size(%zd) is too short", msglen); | ||||
return; | return; | ||||
} | } | ||||
icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; | icp = (struct icmp6_hdr *)iov.iov_base; | ||||
if (icp->icmp6_type != ND_ROUTER_ADVERT) { | if (icp->icmp6_type != ND_ROUTER_ADVERT) { | ||||
/* | /* | ||||
* this should not happen because we configured a filter | * this should not happen because we configured a filter | ||||
* that only passes RAs on the receiving socket. | * that only passes RAs on the receiving socket. | ||||
*/ | */ | ||||
warnmsg(LOG_ERR, __func__, | warnmsg(LOG_ERR, __func__, | ||||
"invalid icmp type(%d) from %s on %s", icp->icmp6_type, | "invalid icmp type(%d) from %s on %s", icp->icmp6_type, | ||||
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, | inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, | ||||
▲ Show 20 Lines • Show All 442 Lines • ▼ Show 20 Lines | if (rao->rao_type == type && | ||||
memcmp(rao->rao_msg, msg, len) == 0) | memcmp(rao->rao_msg, msg, len) == 0) | ||||
break; | break; | ||||
} | } | ||||
return (rao); | return (rao); | ||||
} | } | ||||
static void | static void | ||||
call_script(const int argc, const char *const argv[], | call_script(const char *const argv[], struct script_msg_head_t *sm_head) | ||||
struct script_msg_head_t *sm_head) | |||||
{ | { | ||||
const char *scriptpath; | struct script_msg *smp; | ||||
int fd[2]; | ssize_t len; | ||||
int error; | int status, wfd; | ||||
pid_t pid, wpid; | |||||
if ((scriptpath = argv[0]) == NULL) | wfd = cap_script_run(capscript, argv); | ||||
return; | if (wfd == -1) { | ||||
fd[0] = fd[1] = -1; | |||||
if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { | |||||
error = pipe(fd); | |||||
if (error) { | |||||
warnmsg(LOG_ERR, __func__, | warnmsg(LOG_ERR, __func__, | ||||
"failed to create a pipe: %s", strerror(errno)); | "failed to run script: %s", strerror(errno)); | ||||
return; | return; | ||||
} | } | ||||
} | |||||
/* launch the script */ | if (sm_head != NULL) { | ||||
pid = fork(); | |||||
if (pid < 0) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"failed to fork: %s", strerror(errno)); | |||||
return; | |||||
} else if (pid) { /* parent */ | |||||
int wstatus; | |||||
if (fd[0] != -1) { /* Send message to the child if any. */ | |||||
ssize_t len; | |||||
struct script_msg *smp; | |||||
close(fd[0]); | |||||
TAILQ_FOREACH(smp, sm_head, sm_next) { | TAILQ_FOREACH(smp, sm_head, sm_next) { | ||||
len = strlen(smp->sm_msg); | len = strlen(smp->sm_msg); | ||||
warnmsg(LOG_DEBUG, __func__, | warnmsg(LOG_DEBUG, __func__, "write to child = %s(%zd)", | ||||
"write to child = %s(%zd)", | |||||
smp->sm_msg, len); | smp->sm_msg, len); | ||||
if (write(fd[1], smp->sm_msg, len) != len) { | if (write(wfd, smp->sm_msg, len) != len) { | ||||
warnmsg(LOG_ERR, __func__, | warnmsg(LOG_ERR, __func__, | ||||
"write to child failed: %s", | "write to child failed: %s", | ||||
strerror(errno)); | strerror(errno)); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
close(fd[1]); | |||||
} | } | ||||
do { | |||||
wpid = wait(&wstatus); | |||||
} while (wpid != pid && wpid > 0); | |||||
if (wpid < 0) | (void)close(wfd); | ||||
warnmsg(LOG_ERR, __func__, | |||||
"wait: %s", strerror(errno)); | |||||
else | |||||
warnmsg(LOG_DEBUG, __func__, | |||||
"script \"%s\" terminated", scriptpath); | |||||
} else { /* child */ | |||||
int nullfd; | |||||
char **_argv; | |||||
if (safefile(scriptpath)) { | if (cap_script_wait(capscript, &status) != 0) | ||||
warnmsg(LOG_ERR, __func__, | warnmsg(LOG_ERR, __func__, "wait(): %s", strerror(errno)); | ||||
"script \"%s\" cannot be executed safely", | else | ||||
scriptpath); | warnmsg(LOG_DEBUG, __func__, "script \"%s\" status %d", | ||||
exit(1); | argv[0], status); | ||||
} | |||||
nullfd = open("/dev/null", O_RDWR); | |||||
if (nullfd < 0) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"open /dev/null: %s", strerror(errno)); | |||||
exit(1); | |||||
} | |||||
if (fd[0] != -1) { /* Receive message from STDIN if any. */ | |||||
close(fd[1]); | |||||
if (fd[0] != STDIN_FILENO) { | |||||
/* Connect a pipe read-end to child's STDIN. */ | |||||
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"dup2 STDIN: %s", strerror(errno)); | |||||
exit(1); | |||||
} | |||||
close(fd[0]); | |||||
} | |||||
} else | |||||
dup2(nullfd, STDIN_FILENO); | |||||
dup2(nullfd, STDOUT_FILENO); | |||||
dup2(nullfd, STDERR_FILENO); | |||||
if (nullfd > STDERR_FILENO) | |||||
close(nullfd); | |||||
_argv = malloc(sizeof(*_argv) * argc); | |||||
if (_argv == NULL) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"malloc: %s", strerror(errno)); | |||||
exit(1); | |||||
} | |||||
memcpy(_argv, argv, (size_t)argc); | |||||
execv(scriptpath, (char *const *)_argv); | |||||
warnmsg(LOG_ERR, __func__, "child: exec failed: %s", | |||||
strerror(errno)); | |||||
exit(1); | |||||
} | |||||
return; | |||||
} | |||||
static int | |||||
safefile(const char *path) | |||||
{ | |||||
struct stat s; | |||||
uid_t myuid; | |||||
/* no setuid */ | |||||
if (getuid() != geteuid()) { | |||||
warnmsg(LOG_NOTICE, __func__, | |||||
"setuid'ed execution not allowed\n"); | |||||
return (-1); | |||||
} | |||||
if (lstat(path, &s) != 0) { | |||||
warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", | |||||
strerror(errno)); | |||||
return (-1); | |||||
} | |||||
/* the file must be owned by the running uid */ | |||||
myuid = getuid(); | |||||
if (s.st_uid != myuid) { | |||||
warnmsg(LOG_NOTICE, __func__, | |||||
"%s has invalid owner uid\n", path); | |||||
return (-1); | |||||
} | |||||
switch (s.st_mode & S_IFMT) { | |||||
case S_IFREG: | |||||
break; | |||||
default: | |||||
warnmsg(LOG_NOTICE, __func__, | |||||
"%s is an invalid file type 0x%o\n", | |||||
path, (s.st_mode & S_IFMT)); | |||||
return (-1); | |||||
} | |||||
return (0); | |||||
} | } | ||||
/* Decode domain name label encoding in RFC 1035 Section 3.1 */ | /* Decode domain name label encoding in RFC 1035 Section 3.1 */ | ||||
static size_t | static size_t | ||||
dname_labeldec(char *dst, size_t dlen, const char *src) | dname_labeldec(char *dst, size_t dlen, const char *src) | ||||
{ | { | ||||
size_t len; | size_t len; | ||||
const char *src_origin; | const char *src_origin; | ||||
Show All 28 Lines |
errno or caph_rights_limit.