diff options
author | 2015-07-19 21:01:56 +0000 | |
---|---|---|
committer | 2015-07-19 21:01:56 +0000 | |
commit | 627846caf8baf851ed48eed111b4a2c8dcf64351 (patch) | |
tree | e477183921b71ba6938bd9da502025eff89fb117 | |
parent | Rework label mapping algorithms to be more in line with the RFC. (diff) | |
download | wireguard-openbsd-627846caf8baf851ed48eed111b4a2c8dcf64351.tar.xz wireguard-openbsd-627846caf8baf851ed48eed111b4a2c8dcf64351.zip |
Implement md5 authentication support.
ok claudio@
-rw-r--r-- | usr.sbin/ldpd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpd.conf.5 | 24 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpd.h | 21 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpe.c | 33 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpe.h | 28 | ||||
-rw-r--r-- | usr.sbin/ldpd/neighbor.c | 61 | ||||
-rw-r--r-- | usr.sbin/ldpd/packet.c | 26 | ||||
-rw-r--r-- | usr.sbin/ldpd/parse.y | 78 | ||||
-rw-r--r-- | usr.sbin/ldpd/pfkey.c | 470 | ||||
-rw-r--r-- | usr.sbin/ldpd/printconf.c | 22 |
10 files changed, 745 insertions, 22 deletions
diff --git a/usr.sbin/ldpd/Makefile b/usr.sbin/ldpd/Makefile index 3ccc3e7b136..44324444e04 100644 --- a/usr.sbin/ldpd/Makefile +++ b/usr.sbin/ldpd/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.5 2013/06/04 02:25:28 claudio Exp $ +# $OpenBSD: Makefile,v 1.6 2015/07/19 21:01:56 renato Exp $ PROG= ldpd SRCS= accept.c adjacency.c address.c control.c hello.c init.c interface.c \ keepalive.c kroute.c labelmapping.c lde.c lde_lib.c ldpd.c ldpe.c \ - log.c neighbor.c notification.c packet.c parse.y printconf.c + log.c neighbor.c notification.c packet.c parse.y printconf.c pfkey.c MAN= ldpd.8 ldpd.conf.5 diff --git a/usr.sbin/ldpd/ldpd.conf.5 b/usr.sbin/ldpd/ldpd.conf.5 index fc765bf3758..0a4bace9a70 100644 --- a/usr.sbin/ldpd/ldpd.conf.5 +++ b/usr.sbin/ldpd/ldpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ldpd.conf.5,v 1.15 2015/07/19 20:50:03 renato Exp $ +.\" $OpenBSD: ldpd.conf.5,v 1.16 2015/07/19 21:01:56 renato Exp $ .\" .\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> .\" Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> @@ -31,7 +31,7 @@ daemon implements the Label Distribution Protocol as described in RFC 5036. .Sh SECTIONS The .Nm -config file is divided into four main sections. +config file is divided into five main sections. .Bl -tag -width xxxx .It Sy Macros User-defined variables may be defined and used later, simplifying the @@ -43,6 +43,8 @@ Global settings for Interface-specific parameters. .It Sy Targeted Neighbors Configuration Targeted neighbor specific parameters. +.It Sy Neighbors Configuration +Neighbor-specific parameters. .El .Sh MACROS Much like @@ -139,6 +141,24 @@ The default value is 45. Set the hello interval in seconds. The default value is 5; valid range is 1\-65535. .El +.Sh NEIGHBORS +The +.Ic neighbor +section allows for the configuration of neighbor-specific parameters. Note, +however, that +.Xr ldpd 8 +uses the hello discovery mechanism to discover its neighbors. Without an +underlying adjacency these commands have no effect. +.Bd -literal -offset indent +neighbor A.B.C.D { +} +.Ed +.Pp +Neighbor-specific parameters are listed below. +.Bl -tag -width Ds +.It Ic password Ar secret +Enable TCP MD5 signatures per RFC 5036. +.El .Sh FILES .Bl -tag -width "/etc/ldpd.conf" -compact .It Pa /etc/ldpd.conf diff --git a/usr.sbin/ldpd/ldpd.h b/usr.sbin/ldpd/ldpd.h index ed600251b57..c99784e9fbd 100644 --- a/usr.sbin/ldpd/ldpd.h +++ b/usr.sbin/ldpd/ldpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpd.h,v 1.48 2015/07/19 20:54:17 renato Exp $ */ +/* $OpenBSD: ldpd.h,v 1.49 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -37,6 +37,8 @@ #define LDPD_SOCKET "/var/run/ldpd.sock" #define LDPD_USER "_ldpd" +#define TCP_MD5_KEY_LEN 80 + #define NBR_IDSELF 1 #define NBR_CNTSTART (NBR_IDSELF + 1) @@ -219,6 +221,22 @@ struct tnbr { }; #define F_TNBR_CONFIGURED 0x01 +enum auth_method { + AUTH_NONE, + AUTH_MD5SIG, +}; + +/* neighbor specific parameters */ +struct nbr_params { + LIST_ENTRY(nbr_params) entry; + struct in_addr addr; + struct { + enum auth_method method; + char md5key[TCP_MD5_KEY_LEN]; + u_int8_t md5key_len; + } auth; +}; + /* ldp_conf */ enum { PROC_MAIN, @@ -238,6 +256,7 @@ struct ldpd_conf { LIST_HEAD(, iface) iface_list; LIST_HEAD(, if_addr) addr_list; LIST_HEAD(, tnbr) tnbr_list; + LIST_HEAD(, nbr_params) nbrp_list; u_int32_t opts; #define LDPD_OPT_VERBOSE 0x00000001 diff --git a/usr.sbin/ldpd/ldpe.c b/usr.sbin/ldpd/ldpe.c index d7b4172c760..c4413c164d7 100644 --- a/usr.sbin/ldpd/ldpe.c +++ b/usr.sbin/ldpd/ldpe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpe.c,v 1.31 2015/07/19 20:54:17 renato Exp $ */ +/* $OpenBSD: ldpe.c,v 1.32 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -22,6 +22,7 @@ #include <sys/socket.h> #include <sys/queue.h> #include <netinet/in.h> +#include <netinet/tcp.h> #include <arpa/inet.h> #include <net/if_types.h> #include <stdlib.h> @@ -51,6 +52,8 @@ void ldpe_shutdown(void); struct ldpd_conf *leconf = NULL, *nconf; struct imsgev *iev_main; struct imsgev *iev_lde; +struct event pfkey_ev; +struct ldpd_sysdep sysdep; /* ARGSUSED */ void @@ -77,6 +80,7 @@ ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], struct event ev_sigint, ev_sigterm; struct sockaddr_in disc_addr, sess_addr; pid_t pid; + int pfkeysock, opt; switch (pid = fork()) { case -1: @@ -92,6 +96,8 @@ ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], setproctitle("ldp engine"); ldpd_process = PROC_LDP_ENGINE; + pfkeysock = pfkey_init(&sysdep); + /* create ldpd control socket outside chroot */ if (control_init() == -1) fatalx("control socket setup failed"); @@ -171,6 +177,16 @@ ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], if (listen(xconf->ldp_session_socket, LDP_BACKLOG) == -1) fatal("error in listen on session socket"); + opt = 1; + if (setsockopt(xconf->ldp_session_socket, IPPROTO_TCP, TCP_MD5SIG, + &opt, sizeof(opt)) == -1) { + if (errno == ENOPROTOOPT) { /* system w/o md5sig */ + log_warnx("md5sig not available, disabling"); + sysdep.no_md5sig = 1; + } else + fatal("setsockopt TCP_MD5SIG"); + } + /* set some defaults */ if (if_set_tos(xconf->ldp_session_socket, IPTOS_PREC_INTERNETCONTROL) == -1) @@ -225,6 +241,10 @@ ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], iev_main->handler, iev_main); event_add(&iev_main->ev, NULL); + event_set(&pfkey_ev, pfkeysock, EV_READ | EV_PERSIST, + ldpe_dispatch_pfkey, NULL); + event_add(&pfkey_ev, NULL); + event_set(&leconf->disc_ev, leconf->ldp_discovery_socket, EV_READ|EV_PERSIST, disc_recv_packet, NULL); event_add(&leconf->disc_ev, NULL); @@ -574,6 +594,17 @@ ldpe_dispatch_lde(int fd, short event, void *bula) } } +/* ARGSUSED */ +void +ldpe_dispatch_pfkey(int fd, short event, void *bula) +{ + if (event & EV_READ) { + if (pfkey_read(fd, NULL) == -1) { + fatal("pfkey_read failed, exiting..."); + } + } +} + u_int32_t ldpe_router_id(void) { diff --git a/usr.sbin/ldpd/ldpe.h b/usr.sbin/ldpd/ldpe.h index 65c98a4979d..e5dca3462fb 100644 --- a/usr.sbin/ldpd/ldpe.h +++ b/usr.sbin/ldpd/ldpe.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpe.h,v 1.35 2015/04/04 15:09:47 renato Exp $ */ +/* $OpenBSD: ldpe.h,v 1.36 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> @@ -26,6 +26,7 @@ #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> +#include <net/pfkeyv2.h> TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns; @@ -82,6 +83,15 @@ struct nbr { int state; int idtimer_cnt; u_int16_t keepalive; + + struct { + u_int8_t established; + struct in_addr local_addr; + u_int32_t spi_in; + u_int32_t spi_out; + enum auth_method method; + char md5key[TCP_MD5_KEY_LEN]; + } auth; }; struct mapping_entry { @@ -89,6 +99,11 @@ struct mapping_entry { struct map map; }; +struct ldpd_sysdep { + u_int8_t no_pfkey; + u_int8_t no_md5sig; +}; + /* accept.c */ void accept_init(void); int accept_add(int, void (*)(int, short, void *), void *); @@ -128,6 +143,7 @@ int recv_labelmessage(struct nbr *, char *, u_int16_t, u_int16_t); pid_t ldpe(struct ldpd_conf *, int[2], int[2], int[2]); void ldpe_dispatch_main(int, short, void *); void ldpe_dispatch_lde(int, short, void *); +void ldpe_dispatch_pfkey(int, short, void *); int ldpe_imsg_compose_parent(int, pid_t, void *, u_int16_t); int ldpe_imsg_compose_lde(int, u_int32_t, pid_t, void *, u_int16_t); @@ -199,6 +215,10 @@ void nbr_mapping_add(struct nbr *, struct mapping_head *, struct map *); void mapping_list_clr(struct mapping_head *); + +struct nbr_params *nbr_params_new(struct in_addr); +struct nbr_params *nbr_params_find(struct in_addr); + struct ctl_nbr *nbr_to_ctl(struct nbr *); void ldpe_nbr_ctl(struct ctl_conn *); @@ -219,4 +239,10 @@ void session_shutdown(struct nbr *, u_int32_t, u_int32_t, u_int32_t); char *pkt_ptr; /* packet buffer */ +/* pfkey.c */ +int pfkey_read(int, struct sadb_msg *); +int pfkey_establish(struct nbr *, struct nbr_params *); +int pfkey_remove(struct nbr *); +int pfkey_init(struct ldpd_sysdep *); + #endif /* _LDPE_H_ */ diff --git a/usr.sbin/ldpd/neighbor.c b/usr.sbin/ldpd/neighbor.c index 849516860a1..99b291e3ad5 100644 --- a/usr.sbin/ldpd/neighbor.c +++ b/usr.sbin/ldpd/neighbor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: neighbor.c,v 1.46 2015/07/19 20:50:03 renato Exp $ */ +/* $OpenBSD: neighbor.c,v 1.47 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -23,6 +23,7 @@ #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netinet/tcp.h> #include <arpa/inet.h> #include <net/if.h> @@ -72,7 +73,8 @@ struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid); u_int32_t peercnt = NBR_CNTSTART; -extern struct ldpd_conf *leconf; +extern struct ldpd_conf *leconf; +extern struct ldpd_sysdep sysdep; struct { int state; @@ -208,7 +210,8 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) struct nbr * nbr_new(struct in_addr id, struct in_addr addr) { - struct nbr *nbr; + struct nbr *nbr; + struct nbr_params *nbrp; log_debug("nbr_new: LSR ID %s", inet_ntoa(id)); @@ -240,6 +243,12 @@ nbr_new(struct in_addr id, struct in_addr addr) evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr); evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr); + /* init pfkey - remove old if any, load new ones */ + pfkey_remove(nbr); + nbrp = nbr_params_find(nbr->addr); + if (nbrp && pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); + return (nbr); } @@ -249,6 +258,7 @@ nbr_del(struct nbr *nbr) log_debug("nbr_del: LSR ID %s", inet_ntoa(nbr->id)); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + pfkey_remove(nbr); if (event_pending(&nbr->ev_connect, EV_WRITE, NULL)) event_del(&nbr->ev_connect); @@ -465,9 +475,11 @@ nbr_connect_cb(int fd, short event, void *arg) int nbr_establish_connection(struct nbr *nbr) { - struct sockaddr_in local_sa; - struct sockaddr_in remote_sa; + struct sockaddr_in local_sa; + struct sockaddr_in remote_sa; struct adj *adj; + struct nbr_params *nbrp; + int opt = 1; nbr->fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); if (nbr->fd == -1) { @@ -476,6 +488,19 @@ nbr_establish_connection(struct nbr *nbr) return (-1); } + nbrp = nbr_params_find(nbr->addr); + if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { + if (sysdep.no_pfkey || sysdep.no_md5sig) { + log_warnx("md5sig configured but not available"); + return (-1); + } + if (setsockopt(nbr->fd, IPPROTO_TCP, TCP_MD5SIG, + &opt, sizeof(opt)) == -1) { + log_warn("setsockopt md5sig"); + return (-1); + } + } + bzero(&local_sa, sizeof(local_sa)); local_sa.sin_family = AF_INET; local_sa.sin_port = htons(0); @@ -562,6 +587,32 @@ mapping_list_clr(struct mapping_head *mh) } } +struct nbr_params * +nbr_params_new(struct in_addr addr) +{ + struct nbr_params *nbrp; + + if ((nbrp = calloc(1, sizeof(*nbrp))) == NULL) + fatal("nbr_params_new"); + + nbrp->addr.s_addr = addr.s_addr; + nbrp->auth.method = AUTH_NONE; + + return (nbrp); +} + +struct nbr_params * +nbr_params_find(struct in_addr addr) +{ + struct nbr_params *nbrp; + + LIST_FOREACH(nbrp, &leconf->nbrp_list, entry) + if (nbrp->addr.s_addr == addr.s_addr) + return (nbrp); + + return (NULL); +} + struct ctl_nbr * nbr_to_ctl(struct nbr *nbr) { diff --git a/usr.sbin/ldpd/packet.c b/usr.sbin/ldpd/packet.c index aeafa9e40a8..6ed78055cfc 100644 --- a/usr.sbin/ldpd/packet.c +++ b/usr.sbin/ldpd/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.38 2015/04/04 15:04:49 renato Exp $ */ +/* $OpenBSD: packet.c,v 1.39 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -23,6 +23,7 @@ #include <netinet/in.h> #include <netinet/ip.h> +#include <netinet/tcp.h> #include <arpa/inet.h> #include <net/if_dl.h> #include <fcntl.h> @@ -39,6 +40,7 @@ #include "ldpe.h" extern struct ldpd_conf *leconf; +extern struct ldpd_sysdep sysdep; struct iface *disc_find_iface(unsigned int, struct in_addr); ssize_t session_get_pdu(struct ibuf_read *, char **); @@ -266,6 +268,8 @@ session_accept(int fd, short event, void *bula) struct sockaddr_in src; int newfd; socklen_t len = sizeof(src); + struct nbr_params *nbrp; + int opt; if (!(event & EV_READ)) return; @@ -286,6 +290,26 @@ session_accept(int fd, short event, void *bula) return; } + nbrp = nbr_params_find(src.sin_addr); + if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { + if (sysdep.no_pfkey || sysdep.no_md5sig) { + log_warnx("md5sig configured but not available"); + close(newfd); + return; + } + + len = sizeof(opt); + if (getsockopt(newfd, IPPROTO_TCP, TCP_MD5SIG, + &opt, &len) == -1) + fatal("getsockopt TCP_MD5SIG"); + if (!opt) { /* non-md5'd connection! */ + log_warnx( + "connection attempt without md5 signature"); + close(newfd); + return; + } + } + tcp_new(newfd, NULL); } diff --git a/usr.sbin/ldpd/parse.y b/usr.sbin/ldpd/parse.y index 4a6a9f40d9a..904141bbdf1 100644 --- a/usr.sbin/ldpd/parse.y +++ b/usr.sbin/ldpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.24 2015/07/19 20:50:03 renato Exp $ */ +/* $OpenBSD: parse.y,v 1.25 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> @@ -85,8 +85,9 @@ int host(const char *, struct in_addr *, struct in_addr *); static struct ldpd_conf *conf; static int errors = 0; -struct iface *iface = NULL; -struct tnbr *tnbr = NULL; +struct iface *iface = NULL; +struct tnbr *tnbr = NULL; +struct nbr_params *nbrp = NULL; struct config_defaults { u_int16_t lhello_holdtime; @@ -100,8 +101,9 @@ struct config_defaults ifacedefs; struct config_defaults tnbrdefs; struct config_defaults *defs; -struct iface *conf_get_if(struct kif *); -struct tnbr *conf_get_tnbr(struct in_addr); +struct iface *conf_get_if(struct kif *); +struct tnbr *conf_get_tnbr(struct in_addr); +struct nbr_params *conf_get_nbrp(struct in_addr); typedef struct { union { @@ -118,6 +120,7 @@ typedef struct { %token THELLOHOLDTIME THELLOINTERVAL %token THELLOACCEPT %token KEEPALIVE +%token NEIGHBOR PASSWORD %token EXTTAG %token YES NO %token ERROR @@ -134,6 +137,7 @@ grammar : /* empty */ | grammar varset '\n' | grammar interface '\n' | grammar tneighbor '\n' + | grammar neighbor '\n' | grammar error '\n' { file->errors++; } ; @@ -238,6 +242,21 @@ tnbr_defaults : THELLOHOLDTIME NUMBER { } ; +nbr_opts : PASSWORD STRING { + if (strlcpy(nbrp->auth.md5key, $2, + sizeof(nbrp->auth.md5key)) >= + sizeof(nbrp->auth.md5key)) { + yyerror("tcp md5sig password too long: max %zu", + sizeof(nbrp->auth.md5key) - 1); + free($2); + YYERROR; + } + nbrp->auth.md5key_len = strlen($2); + nbrp->auth.method = AUTH_MD5SIG; + free($2); + } + ; + optnl : '\n' optnl | ; @@ -321,6 +340,35 @@ tneighboropts_l : tneighboropts_l tnbr_defaults nl | tnbr_defaults optnl ; +neighbor : NEIGHBOR STRING { + struct in_addr addr; + + if (inet_aton($2, &addr) == 0) { + yyerror( + "error parsing neighbor address"); + free($2); + YYERROR; + } + free($2); + + nbrp = conf_get_nbrp(addr); + if (nbrp == NULL) + YYERROR; + LIST_INSERT_HEAD(&conf->nbrp_list, nbrp, entry); + } neighbor_block { + nbrp = NULL; + } + ; + +neighbor_block : '{' optnl neighboropts_l '}' + | '{' optnl '}' + | /* nothing */ + ; + +neighboropts_l : neighboropts_l nbr_opts nl + | nbr_opts optnl + ; + %% struct keywords { @@ -361,7 +409,9 @@ lookup(char *s) {"keepalive", KEEPALIVE}, {"link-hello-holdtime", LHELLOHOLDTIME}, {"link-hello-interval", LHELLOINTERVAL}, + {"neighbor", NEIGHBOR}, {"no", NO}, + {"password", PASSWORD}, {"router-id", ROUTERID}, {"targeted-hello-accept", THELLOACCEPT}, {"targeted-hello-holdtime", THELLOHOLDTIME}, @@ -853,6 +903,24 @@ conf_get_tnbr(struct in_addr addr) return (t); } +struct nbr_params * +conf_get_nbrp(struct in_addr addr) +{ + struct nbr_params *n; + + LIST_FOREACH(n, &conf->nbrp_list, entry) { + if (n->addr.s_addr == addr.s_addr) { + yyerror("neighbor %s already configured", + inet_ntoa(addr)); + return (NULL); + } + } + + n = nbr_params_new(addr); + + return (n); +} + void clear_config(struct ldpd_conf *xconf) { diff --git a/usr.sbin/ldpd/pfkey.c b/usr.sbin/ldpd/pfkey.c new file mode 100644 index 00000000000..0a846905ed2 --- /dev/null +++ b/usr.sbin/ldpd/pfkey.c @@ -0,0 +1,470 @@ +/* $OpenBSD: pfkey.c,v 1.1 2015/07/19 21:01:56 renato Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <net/pfkeyv2.h> +#include <netinet/ip_ipsp.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +#define PFKEY2_CHUNK sizeof(u_int64_t) +#define ROUNDUP(x) (((x) + (PFKEY2_CHUNK - 1)) & ~(PFKEY2_CHUNK - 1)) +#define IOV_CNT 20 + +static u_int32_t sadb_msg_seq = 0; +static u_int32_t pid = 0; /* should pid_t but pfkey needs u_int32_t */ +static int fd; + +int pfkey_reply(int, u_int32_t *); +int pfkey_send(int, uint8_t, uint8_t, uint8_t, + struct in_addr *, struct in_addr *, + u_int32_t, uint8_t, int, char *, uint8_t, int, char *, + uint16_t, uint16_t); +int pfkey_sa_add(struct in_addr *, struct in_addr *, u_int8_t, char *, + u_int32_t *); +int pfkey_sa_remove(struct in_addr *, struct in_addr *, u_int32_t *); + +int pfkey_md5sig_establish(struct nbr *, struct nbr_params *nbrp); +int pfkey_md5sig_remove(struct nbr *); + +static struct sockaddr * +addr2sa(struct in_addr *addr) +{ + static struct sockaddr_storage ss; + struct sockaddr_in *sa_in = (struct sockaddr_in *)&ss; + + bzero(&ss, sizeof(ss)); + sa_in->sin_family = AF_INET; + sa_in->sin_len = sizeof(struct sockaddr_in); + sa_in->sin_addr.s_addr = addr->s_addr; + sa_in->sin_port = htons(0); + + return ((struct sockaddr *)&ss); +} + +int +pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir, + struct in_addr *src, struct in_addr *dst, u_int32_t spi, + uint8_t aalg, int alen, char *akey, uint8_t ealg, int elen, char *ekey, + uint16_t sport, uint16_t dport) +{ + struct sadb_msg smsg; + struct sadb_sa sa; + struct sadb_address sa_src, sa_dst; + struct sadb_key sa_akey, sa_ekey; + struct sadb_spirange sa_spirange; + struct iovec iov[IOV_CNT]; + ssize_t n; + int len = 0; + int iov_cnt; + struct sockaddr_storage ssrc, sdst, smask, dmask; + struct sockaddr *saptr; + + if (!pid) + pid = getpid(); + + /* we need clean sockaddr... no ports set */ + bzero(&ssrc, sizeof(ssrc)); + bzero(&smask, sizeof(smask)); + if ((saptr = addr2sa(src))) + memcpy(&ssrc, saptr, sizeof(ssrc)); + memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8); + smask.ss_family = ssrc.ss_family; + smask.ss_len = ssrc.ss_len; + + bzero(&sdst, sizeof(sdst)); + bzero(&dmask, sizeof(dmask)); + if ((saptr = addr2sa(dst))) + memcpy(&sdst, saptr, sizeof(sdst)); + memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8); + dmask.ss_family = sdst.ss_family; + dmask.ss_len = sdst.ss_len; + + bzero(&smsg, sizeof(smsg)); + smsg.sadb_msg_version = PF_KEY_V2; + smsg.sadb_msg_seq = ++sadb_msg_seq; + smsg.sadb_msg_pid = pid; + smsg.sadb_msg_len = sizeof(smsg) / 8; + smsg.sadb_msg_type = mtype; + smsg.sadb_msg_satype = satype; + + switch (mtype) { + case SADB_GETSPI: + bzero(&sa_spirange, sizeof(sa_spirange)); + sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; + sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8; + sa_spirange.sadb_spirange_min = 0x100; + sa_spirange.sadb_spirange_max = 0xffffffff; + sa_spirange.sadb_spirange_reserved = 0; + break; + case SADB_ADD: + case SADB_UPDATE: + case SADB_DELETE: + bzero(&sa, sizeof(sa)); + sa.sadb_sa_exttype = SADB_EXT_SA; + sa.sadb_sa_len = sizeof(sa) / 8; + sa.sadb_sa_replay = 0; + sa.sadb_sa_spi = spi; + sa.sadb_sa_state = SADB_SASTATE_MATURE; + break; + } + + bzero(&sa_src, sizeof(sa_src)); + sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; + sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8; + + bzero(&sa_dst, sizeof(sa_dst)); + sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; + sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8; + + sa.sadb_sa_auth = aalg; + sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */ + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + bzero(&sa_akey, sizeof(sa_akey)); + sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH; + sa_akey.sadb_key_len = (sizeof(sa_akey) + + ((alen + 7) / 8) * 8) / 8; + sa_akey.sadb_key_bits = 8 * alen; + + bzero(&sa_ekey, sizeof(sa_ekey)); + sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; + sa_ekey.sadb_key_len = (sizeof(sa_ekey) + + ((elen + 7) / 8) * 8) / 8; + sa_ekey.sadb_key_bits = 8 * elen; + + break; + } + + iov_cnt = 0; + + /* msghdr */ + iov[iov_cnt].iov_base = &smsg; + iov[iov_cnt].iov_len = sizeof(smsg); + iov_cnt++; + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + case SADB_DELETE: + /* SA hdr */ + iov[iov_cnt].iov_base = &sa; + iov[iov_cnt].iov_len = sizeof(sa); + smsg.sadb_msg_len += sa.sadb_sa_len; + iov_cnt++; + break; + case SADB_GETSPI: + /* SPI range */ + iov[iov_cnt].iov_base = &sa_spirange; + iov[iov_cnt].iov_len = sizeof(sa_spirange); + smsg.sadb_msg_len += sa_spirange.sadb_spirange_len; + iov_cnt++; + break; + } + + /* dest addr */ + iov[iov_cnt].iov_base = &sa_dst; + iov[iov_cnt].iov_len = sizeof(sa_dst); + iov_cnt++; + iov[iov_cnt].iov_base = &sdst; + iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len); + smsg.sadb_msg_len += sa_dst.sadb_address_len; + iov_cnt++; + + /* src addr */ + iov[iov_cnt].iov_base = &sa_src; + iov[iov_cnt].iov_len = sizeof(sa_src); + iov_cnt++; + iov[iov_cnt].iov_base = &ssrc; + iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len); + smsg.sadb_msg_len += sa_src.sadb_address_len; + iov_cnt++; + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + if (alen) { + /* auth key */ + iov[iov_cnt].iov_base = &sa_akey; + iov[iov_cnt].iov_len = sizeof(sa_akey); + iov_cnt++; + iov[iov_cnt].iov_base = akey; + iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8; + smsg.sadb_msg_len += sa_akey.sadb_key_len; + iov_cnt++; + } + if (elen) { + /* encryption key */ + iov[iov_cnt].iov_base = &sa_ekey; + iov[iov_cnt].iov_len = sizeof(sa_ekey); + iov_cnt++; + iov[iov_cnt].iov_base = ekey; + iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8; + smsg.sadb_msg_len += sa_ekey.sadb_key_len; + iov_cnt++; + } + break; + } + + len = smsg.sadb_msg_len * 8; + do { + n = writev(sd, iov, iov_cnt); + } while (n == -1 && (errno == EAGAIN || errno == EINTR)); + + if (n == -1) { + log_warn("writev (%d/%d)", iov_cnt, len); + return (-1); + } + + return (0); +} + +int +pfkey_read(int sd, struct sadb_msg *h) +{ + struct sadb_msg hdr; + + if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { + if (errno == EAGAIN || errno == EINTR) + return (0); + log_warn("pfkey peek"); + return (-1); + } + + /* XXX: Only one message can be outstanding. */ + if (hdr.sadb_msg_seq == sadb_msg_seq && + hdr.sadb_msg_pid == pid) { + if (h) + bcopy(&hdr, h, sizeof(hdr)); + return (0); + } + + /* not ours, discard */ + if (read(sd, &hdr, sizeof(hdr)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return (0); + log_warn("pfkey read"); + return (-1); + } + + return (1); +} + +int +pfkey_reply(int sd, u_int32_t *spip) +{ + struct sadb_msg hdr, *msg; + struct sadb_ext *ext; + struct sadb_sa *sa; + u_int8_t *data; + ssize_t len; + int rv; + + do { + rv = pfkey_read(sd, &hdr); + if (rv == -1) + return (-1); + } while (rv); + + if (hdr.sadb_msg_errno != 0) { + errno = hdr.sadb_msg_errno; + if (errno == ESRCH) + return (0); + else { + log_warn("pfkey"); + return (-1); + } + } + if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEY2_CHUNK)) == NULL) { + log_warn("pfkey malloc"); + return (-1); + } + len = hdr.sadb_msg_len * PFKEY2_CHUNK; + if (read(sd, data, len) != len) { + log_warn("pfkey read"); + bzero(data, len); + free(data); + return (-1); + } + + if (hdr.sadb_msg_type == SADB_GETSPI) { + if (spip == NULL) { + bzero(data, len); + free(data); + return (0); + } + + msg = (struct sadb_msg *)data; + for (ext = (struct sadb_ext *)(msg + 1); + (size_t)((u_int8_t *)ext - (u_int8_t *)msg) < + msg->sadb_msg_len * PFKEY2_CHUNK; + ext = (struct sadb_ext *)((u_int8_t *)ext + + ext->sadb_ext_len * PFKEY2_CHUNK)) { + if (ext->sadb_ext_type == SADB_EXT_SA) { + sa = (struct sadb_sa *) ext; + *spip = sa->sadb_sa_spi; + break; + } + } + } + bzero(data, len); + free(data); + return (0); +} + +int +pfkey_sa_add(struct in_addr *src, struct in_addr *dst, u_int8_t keylen, + char *key, u_int32_t *spi) +{ + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_GETSPI, 0, + src, dst, 0, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, spi) < 0) + return (-1); + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_UPDATE, 0, + src, dst, *spi, 0, keylen, key, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, NULL) < 0) + return (-1); + return (0); +} + +int +pfkey_sa_remove(struct in_addr *src, struct in_addr *dst, u_int32_t *spi) +{ + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_DELETE, 0, + src, dst, *spi, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, NULL) < 0) + return (-1); + *spi = 0; + return (0); +} + +int +pfkey_md5sig_establish(struct nbr *nbr, struct nbr_params *nbrp) +{ + sleep(1); + + if (!nbr->auth.spi_out) + if (pfkey_sa_add(&nbr->auth.local_addr, &nbr->addr, + nbrp->auth.md5key_len, nbrp->auth.md5key, + &nbr->auth.spi_out) == -1) + return (-1); + if (!nbr->auth.spi_in) + if (pfkey_sa_add(&nbr->addr, &nbr->auth.local_addr, + nbrp->auth.md5key_len, nbrp->auth.md5key, + &nbr->auth.spi_in) == -1) + return (-1); + + nbr->auth.established = 1; + return (0); +} + +int +pfkey_md5sig_remove(struct nbr *nbr) +{ + if (nbr->auth.spi_out) + if (pfkey_sa_remove(&nbr->auth.local_addr, &nbr->addr, + &nbr->auth.spi_out) == -1) + return (-1); + if (nbr->auth.spi_in) + if (pfkey_sa_remove(&nbr->addr, &nbr->auth.local_addr, + &nbr->auth.spi_in) == -1) + return (-1); + + nbr->auth.established = 0; + nbr->auth.local_addr.s_addr = 0; + nbr->auth.spi_in = 0; + nbr->auth.spi_out = 0; + nbr->auth.method = AUTH_NONE; + memset(nbr->auth.md5key, 0, sizeof(nbr->auth.md5key)); + + return (0); +} + +int +pfkey_establish(struct nbr *nbr, struct nbr_params *nbrp) +{ + if (nbrp->auth.method == AUTH_NONE) + return (0); + + /* + * make sure we keep copies of everything we need to + * remove SAs and flows later again. + */ + nbr->auth.local_addr.s_addr = ldpe_router_id(); + nbr->auth.method = nbrp->auth.method; + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + strlcpy(nbr->auth.md5key, nbrp->auth.md5key, + sizeof(nbr->auth.md5key)); + return (pfkey_md5sig_establish(nbr, nbrp)); + break; + default: + break; + } + + return (0); +} + +int +pfkey_remove(struct nbr *nbr) +{ + if (nbr->auth.method == AUTH_NONE || !nbr->auth.established) + return (0); + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + return (pfkey_md5sig_remove(nbr)); + break; + default: + break; + } + + return (0); +} + +int +pfkey_init(struct ldpd_sysdep *sysdep) +{ + if ((fd = socket(PF_KEY, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_KEY_V2)) == -1) { + if (errno == EPROTONOSUPPORT) { + log_warnx("PF_KEY not available, disabling ipsec"); + sysdep->no_pfkey = 1; + return (-1); + } else + fatal("pfkey setup failed"); + } + return (fd); +} diff --git a/usr.sbin/ldpd/printconf.c b/usr.sbin/ldpd/printconf.c index 626b6745359..6a89d813a77 100644 --- a/usr.sbin/ldpd/printconf.c +++ b/usr.sbin/ldpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.8 2015/07/19 20:50:03 renato Exp $ */ +/* $OpenBSD: printconf.c,v 1.9 2015/07/19 21:01:56 renato Exp $ */ /* * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> @@ -30,7 +30,8 @@ void print_mainconf(struct ldpd_conf *); void print_iface(struct iface *); -void print_tnbr(struct tnbr *tnbr); +void print_tnbr(struct tnbr *); +void print_nbrp(struct nbr_params *); void print_mainconf(struct ldpd_conf *conf) @@ -69,10 +70,20 @@ print_tnbr(struct tnbr *tnbr) } void +print_nbrp(struct nbr_params *nbrp) +{ + printf("\nneighbor %s {\n", inet_ntoa(nbrp->addr)); + if (nbrp->auth.method == AUTH_MD5SIG) + printf("\tpassword XXXXXX\n"); + printf("}\n"); +} + +void print_config(struct ldpd_conf *conf) { - struct iface *iface; - struct tnbr *tnbr; + struct iface *iface; + struct tnbr *tnbr; + struct nbr_params *nbrp; print_mainconf(conf); printf("\n"); @@ -82,4 +93,7 @@ print_config(struct ldpd_conf *conf) printf("\n"); LIST_FOREACH(tnbr, &conf->tnbr_list, entry) print_tnbr(tnbr); + printf("\n"); + LIST_FOREACH(nbrp, &conf->nbrp_list, entry) + print_nbrp(nbrp); } |