xref: /freebsd/crypto/krb5/src/lib/apputils/net-server.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */
3 /*
4  * Copyright 1990,2000,2007,2008,2009,2010,2016 by the Massachusetts Institute
5  * of Technology.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "adm_proto.h"
29 #include <sys/ioctl.h>
30 #include <syslog.h>
31 
32 #include <stddef.h>
33 #include "port-sockets.h"
34 #include "socket-utils.h"
35 
36 #include <gssrpc/rpc.h>
37 
38 #ifdef HAVE_NETINET_IN_H
39 #include <sys/types.h>
40 #include <netinet/in.h>
41 #include <sys/socket.h>
42 #include <sys/un.h>
43 #ifdef HAVE_SYS_SOCKIO_H
44 /* for SIOCGIFCONF, etc. */
45 #include <sys/sockio.h>
46 #endif
47 #include <sys/time.h>
48 #if HAVE_SYS_SELECT_H
49 #include <sys/select.h>
50 #endif
51 #include <arpa/inet.h>
52 
53 #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
54 #include <net/if.h>
55 #endif
56 
57 #ifdef HAVE_SYS_FILIO_H
58 #include <sys/filio.h>          /* FIONBIO */
59 #endif
60 
61 #include "fake-addrinfo.h"
62 #include "net-server.h"
63 #include <signal.h>
64 #include <netdb.h>
65 
66 #include "udppktinfo.h"
67 
68 /* List of systemd socket activation addresses and socket types. */
69 struct sockact_list {
70     size_t nsockets;
71     struct {
72         struct sockaddr_storage addr;
73         int type;
74     } *fds;
75 };
76 
77 /* When systemd socket activation is used, caller-provided sockets begin at
78  * file descriptor 3. */
79 const int SOCKACT_START = 3;
80 
81 /* XXX */
82 #define KDC5_NONET                               (-1779992062L)
83 
84 static int stream_data_counter;
85 static int max_stream_data_connections = 45;
86 
87 static int
setreuseaddr(int sock,int value)88 setreuseaddr(int sock, int value)
89 {
90     int st;
91 
92     st = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
93     if (st)
94         return st;
95 #if defined(SO_REUSEPORT) && defined(__APPLE__)
96     /* macOS experimentally needs this flag as well to avoid conflicts between
97      * recently exited server processes and new ones. */
98     st = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
99     if (st)
100         return st;
101 #endif
102     return 0;
103 }
104 
105 #if defined(IPV6_V6ONLY)
106 static int
setv6only(int sock,int value)107 setv6only(int sock, int value)
108 {
109     return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
110 }
111 #endif
112 
113 /* KDC data.  */
114 
115 enum conn_type {
116     CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC,
117     CONN_UNIXSOCK_LISTENER, CONN_UNIXSOCK
118 };
119 
120 static const char *const conn_type_names[] = {
121     [CONN_UDP] = "UDP",
122     [CONN_TCP_LISTENER] = "TCP listener",
123     [CONN_TCP] = "TCP",
124     [CONN_RPC_LISTENER] = "RPC listener",
125     [CONN_RPC] = "RPC",
126     [CONN_UNIXSOCK_LISTENER] = "UNIX domain socket listener",
127     [CONN_UNIXSOCK] = "UNIX domain socket"
128 };
129 
130 enum bind_type {
131     UDP, TCP, RPC, UNX
132 };
133 
134 static const char *const bind_type_names[] = {
135     [UDP] = "UDP",
136     [TCP] = "TCP",
137     [RPC] = "RPC",
138     [UNX] = "UNIXSOCK",
139 };
140 
141 /* Per-connection info.  */
142 struct connection {
143     void *handle;
144     const char *prog;
145     enum conn_type type;
146 
147     /* Connection fields (TCP or RPC) */
148     struct sockaddr_storage addr_s;
149     socklen_t addrlen;
150     char addrbuf[128];
151 
152     /* Incoming data (TCP) */
153     size_t bufsiz;
154     size_t offset;
155     char *buffer;
156     size_t msglen;
157 
158     /* Outgoing data (TCP) */
159     krb5_data *response;
160     unsigned char lenbuf[4];
161     sg_buf sgbuf[2];
162     sg_buf *sgp;
163     int sgnum;
164 
165     /* Crude denial-of-service avoidance support (TCP or RPC) */
166     time_t start_time;
167 
168     /* RPC-specific fields */
169     SVCXPRT *transp;
170     int rpc_force_close;
171 };
172 
173 #define SET(TYPE) struct { TYPE *data; size_t n, max; }
174 
175 /* Start at the top and work down -- this should allow for deletions
176    without disrupting the iteration, since we delete by overwriting
177    the element to be removed with the last element.  */
178 #define FOREACH_ELT(set,idx,vvar)                                       \
179     for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
180 
181 #define GROW_SET(set, incr, tmpptr)                                     \
182     ((set.max + incr < set.max                                          \
183       || ((set.max + incr) * sizeof(set.data[0]) / sizeof(set.data[0])  \
184           != set.max + incr))                                           \
185      ? 0                         /* overflow */                         \
186      : ((tmpptr = realloc(set.data,                                     \
187                           (set.max + incr) * sizeof(set.data[0])))      \
188         ? (set.data = tmpptr, set.max += incr, 1)                       \
189         : 0))
190 
191 /* 1 = success, 0 = failure */
192 #define ADD(set, val, tmpptr)                           \
193     ((set.n < set.max || GROW_SET(set, 10, tmpptr))     \
194      ? (set.data[set.n++] = val, 1)                     \
195      : 0)
196 
197 #define DEL(set, idx)                           \
198     (set.data[idx] = set.data[--set.n], 0)
199 
200 #define FREE_SET_DATA(set)                                      \
201     (free(set.data), set.data = 0, set.max = 0, set.n = 0)
202 
203 /*
204  * N.B.: The Emacs cc-mode indentation code seems to get confused if
205  * the macro argument here is one word only.  So use "unsigned short"
206  * instead of the "u_short" we were using before.
207  */
208 struct rpc_svc_data {
209     u_long prognum;
210     u_long versnum;
211     void (*dispatch)(struct svc_req *, SVCXPRT *);
212 };
213 
214 struct bind_address {
215     char *address;
216     u_short port;
217     enum bind_type type;
218     struct rpc_svc_data rpc_svc_data;
219 };
220 
221 static SET(verto_ev *) events;
222 static SET(struct bind_address) bind_addresses;
223 
224 verto_ctx *
loop_init(verto_ev_type types)225 loop_init(verto_ev_type types)
226 {
227     types |= VERTO_EV_TYPE_IO;
228     types |= VERTO_EV_TYPE_SIGNAL;
229     types |= VERTO_EV_TYPE_TIMEOUT;
230     return verto_default(NULL, types);
231 }
232 
233 static void
do_break(verto_ctx * ctx,verto_ev * ev)234 do_break(verto_ctx *ctx, verto_ev *ev)
235 {
236     krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));
237     verto_break(ctx);
238 }
239 
240 struct sighup_context {
241     void *handle;
242     void (*reset)(void *);
243 };
244 
245 static void
do_reset(verto_ctx * ctx,verto_ev * ev)246 do_reset(verto_ctx *ctx, verto_ev *ev)
247 {
248     struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev);
249 
250     krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset"));
251     krb5_klog_reopen(get_context(sc->handle));
252     if (sc->reset)
253         sc->reset(sc->handle);
254 }
255 
256 static void
free_sighup_context(verto_ctx * ctx,verto_ev * ev)257 free_sighup_context(verto_ctx *ctx, verto_ev *ev)
258 {
259     free(verto_get_private(ev));
260 }
261 
262 krb5_error_code
loop_setup_signals(verto_ctx * ctx,void * handle,void (* reset)(void *))263 loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)(void *))
264 {
265     struct sighup_context *sc;
266     verto_ev *ev;
267 
268     if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT)  ||
269         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) ||
270         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) ||
271         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE))
272         return ENOMEM;
273 
274     ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP);
275     if (!ev)
276         return ENOMEM;
277 
278     sc = malloc(sizeof(*sc));
279     if (!sc)
280         return ENOMEM;
281 
282     sc->handle = handle;
283     sc->reset = reset;
284     verto_set_private(ev, sc, free_sighup_context);
285     return 0;
286 }
287 
288 /*
289  * Add a bind address to the loop.
290  *
291  * Arguments:
292  * - address
293  *      An address string, hostname, or UNIX socket path.
294  *      Pass NULL to use the wildcard address for IP sockets.
295  * - port
296  *      What port the socket should be set to (for IPv4 or IPv6).
297  * - type
298  *      bind_type for the socket.
299  * - rpc_data
300  *      For RPC addresses, the svc_register() arguments to use when TCP
301  *      connections are created.  Ignored for other types.
302  */
303 static krb5_error_code
loop_add_address(const char * address,int port,enum bind_type type,struct rpc_svc_data * rpc_data)304 loop_add_address(const char *address, int port, enum bind_type type,
305                  struct rpc_svc_data *rpc_data)
306 {
307     struct bind_address addr, val;
308     int i;
309     void *tmp;
310     char *addr_copy = NULL;
311 
312     assert(!(type == RPC && rpc_data == NULL));
313 
314     /* Make sure a valid port number was passed. */
315     if (port < 0 || port > 65535) {
316         krb5_klog_syslog(LOG_ERR, _("Invalid port %d"), port);
317         return EINVAL;
318     }
319 
320     /* Check for conflicting addresses. */
321     FOREACH_ELT(bind_addresses, i, val) {
322         if (type != val.type || port != val.port)
323             continue;
324 
325         /* If a wildcard address is being added, make sure to remove any direct
326          * addresses. */
327         if (address == NULL && val.address != NULL) {
328             krb5_klog_syslog(LOG_DEBUG,
329                              _("Removing address %s since wildcard address"
330                                " is being added"),
331                              val.address);
332             free(val.address);
333             DEL(bind_addresses, i);
334         } else if (val.address == NULL || !strcmp(address, val.address)) {
335             krb5_klog_syslog(LOG_DEBUG,
336                              _("Address already added to server"));
337             return 0;
338         }
339     }
340 
341     /* Copy the address if it is specified. */
342     if (address != NULL) {
343         addr_copy = strdup(address);
344         if (addr_copy == NULL)
345             return ENOMEM;
346     }
347 
348     /* Add the new address to bind_addresses. */
349     memset(&addr, 0, sizeof(addr));
350     addr.address = addr_copy;
351     addr.port = port;
352     addr.type = type;
353     if (rpc_data != NULL)
354         addr.rpc_svc_data = *rpc_data;
355     if (!ADD(bind_addresses, addr, tmp)) {
356         free(addr_copy);
357         return ENOMEM;
358     }
359 
360     return 0;
361 }
362 
363 /*
364  * Add bind addresses to the loop.
365  *
366  * Arguments:
367  *
368  * - addresses
369  *      A string for the addresses.  Pass NULL to use the wildcard address.
370  *      Supported delimiters can be found in ADDRESSES_DELIM.  Addresses are
371  *      parsed with k5_parse_host_name().
372  * - default_port
373  *      What port the socket should be set to if not specified in addresses.
374  * - type
375  *      bind_type for the socket.
376  * - rpc_data
377  *      For RPC addresses, the svc_register() arguments to use when TCP
378  *      connections are created.  Ignored for other types.
379  */
380 static krb5_error_code
loop_add_addresses(const char * addresses,int default_port,enum bind_type type,struct rpc_svc_data * rpc_data)381 loop_add_addresses(const char *addresses, int default_port,
382                    enum bind_type type, struct rpc_svc_data *rpc_data)
383 {
384     krb5_error_code ret = 0;
385     char *addresses_copy = NULL, *host = NULL, *saveptr, *addr;
386     int port;
387 
388     /* If no addresses are set, add a wildcard address. */
389     if (addresses == NULL)
390         return loop_add_address(NULL, default_port, type, rpc_data);
391 
392     /* Copy the addresses string before using strtok(). */
393     addresses_copy = strdup(addresses);
394     if (addresses_copy == NULL) {
395         ret = ENOMEM;
396         goto cleanup;
397     }
398 
399     /* Loop through each address in the string and add it to the loop. */
400     addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr);
401     for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) {
402         if (type == UNX) {
403             /* Skip non-pathnames when binding UNIX domain sockets. */
404             if (*addr != '/')
405                 continue;
406             ret = loop_add_address(addr, 0, type, rpc_data);
407             if (ret)
408                 goto cleanup;
409             continue;
410         } else if (*addr == '/') {
411             /* Skip pathnames when not binding UNIX domain sockets. */
412             continue;
413         }
414 
415         /* Parse the host string. */
416         ret = k5_parse_host_string(addr, default_port, &host, &port);
417         if (ret)
418             goto cleanup;
419 
420         ret = loop_add_address(host, port, type, rpc_data);
421         if (ret)
422             goto cleanup;
423 
424         free(host);
425         host = NULL;
426     }
427 
428     ret = 0;
429 cleanup:
430     free(addresses_copy);
431     free(host);
432     return ret;
433 }
434 
435 krb5_error_code
loop_add_udp_address(int default_port,const char * addresses)436 loop_add_udp_address(int default_port, const char *addresses)
437 {
438     return loop_add_addresses(addresses, default_port, UDP, NULL);
439 }
440 
441 krb5_error_code
loop_add_tcp_address(int default_port,const char * addresses)442 loop_add_tcp_address(int default_port, const char *addresses)
443 {
444     return loop_add_addresses(addresses, default_port, TCP, NULL);
445 }
446 
447 krb5_error_code
loop_add_rpc_service(int default_port,const char * addresses,u_long prognum,u_long versnum,void (* dispatchfn)(struct svc_req *,SVCXPRT *))448 loop_add_rpc_service(int default_port, const char *addresses, u_long prognum,
449                      u_long versnum,
450                      void (*dispatchfn)(struct svc_req *, SVCXPRT *))
451 {
452     struct rpc_svc_data svc;
453 
454     svc.prognum = prognum;
455     svc.versnum = versnum;
456     svc.dispatch = dispatchfn;
457     return loop_add_addresses(addresses, default_port, RPC, &svc);
458 }
459 
460 krb5_error_code
loop_add_unix_socket(const char * socket_paths)461 loop_add_unix_socket(const char *socket_paths)
462 {
463     /* There is no wildcard or default UNIX domain socket. */
464     if (socket_paths == NULL)
465         return 0;
466 
467     return loop_add_addresses(socket_paths, 0, UNX, NULL);
468 }
469 
470 #define USE_AF AF_INET
471 #define USE_TYPE SOCK_DGRAM
472 #define USE_PROTO 0
473 #define SOCKET_ERRNO errno
474 #include "foreachaddr.h"
475 
476 static void
free_connection(struct connection * conn)477 free_connection(struct connection *conn)
478 {
479     if (!conn)
480         return;
481     if (conn->response)
482         krb5_free_data(get_context(conn->handle), conn->response);
483     if (conn->buffer)
484         free(conn->buffer);
485     if (conn->type == CONN_RPC_LISTENER && conn->transp != NULL)
486         svc_destroy(conn->transp);
487     free(conn);
488 }
489 
490 static void
remove_event_from_set(verto_ev * ev)491 remove_event_from_set(verto_ev *ev)
492 {
493     verto_ev *tmp;
494     int i;
495 
496     /* Remove the event from the events. */
497     FOREACH_ELT(events, i, tmp)
498         if (tmp == ev) {
499             DEL(events, i);
500             break;
501         }
502 }
503 
504 static void
free_socket(verto_ctx * ctx,verto_ev * ev)505 free_socket(verto_ctx *ctx, verto_ev *ev)
506 {
507     struct connection *conn = NULL;
508     fd_set fds;
509     int fd;
510 
511     remove_event_from_set(ev);
512 
513     fd = verto_get_fd(ev);
514     conn = verto_get_private(ev);
515 
516     /* Close the file descriptor. */
517     krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd);
518     if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->rpc_force_close))
519         close(fd);
520 
521     /* Free the connection struct. */
522     if (conn) {
523         switch (conn->type) {
524         case CONN_RPC:
525             if (conn->rpc_force_close) {
526                 FD_ZERO(&fds);
527                 FD_SET(fd, &fds);
528                 svc_getreqset(&fds);
529                 if (FD_ISSET(fd, &svc_fdset)) {
530                     krb5_klog_syslog(LOG_ERR,
531                                      _("descriptor %d closed but still "
532                                        "in svc_fdset"),
533                                      fd);
534                 }
535             }
536             /* Fall through. */
537         case CONN_TCP:
538         case CONN_UNIXSOCK:
539             stream_data_counter--;
540             break;
541         default:
542             break;
543         }
544 
545         free_connection(conn);
546     }
547 }
548 
549 static verto_ev *
make_event(verto_ctx * ctx,verto_ev_flag flags,verto_callback callback,int sock,struct connection * conn)550 make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback,
551            int sock, struct connection *conn)
552 {
553     verto_ev *ev;
554     void *tmp;
555 
556     ev = verto_add_io(ctx, flags, callback, sock);
557     if (!ev) {
558         com_err(conn->prog, ENOMEM, _("cannot create io event"));
559         return NULL;
560     }
561 
562     if (!ADD(events, ev, tmp)) {
563         com_err(conn->prog, ENOMEM, _("cannot save event"));
564         verto_del(ev);
565         return NULL;
566     }
567 
568     verto_set_private(ev, conn, free_socket);
569     return ev;
570 }
571 
572 static krb5_error_code
add_fd(int sock,enum conn_type conntype,verto_ev_flag flags,void * handle,const char * prog,verto_ctx * ctx,verto_callback callback,verto_ev ** ev_out)573 add_fd(int sock, enum conn_type conntype, verto_ev_flag flags, void *handle,
574        const char *prog, verto_ctx *ctx, verto_callback callback,
575        verto_ev **ev_out)
576 {
577     struct connection *newconn;
578 
579     *ev_out = NULL;
580 
581 #ifndef _WIN32
582     if (sock >= FD_SETSIZE) {
583         com_err(prog, 0, _("file descriptor number %d too high"), sock);
584         return EMFILE;
585     }
586 #endif
587     newconn = malloc(sizeof(*newconn));
588     if (newconn == NULL) {
589         com_err(prog, ENOMEM,
590                 _("cannot allocate storage for connection info"));
591         return ENOMEM;
592     }
593     memset(newconn, 0, sizeof(*newconn));
594     newconn->handle = handle;
595     newconn->prog = prog;
596     newconn->type = conntype;
597 
598     *ev_out = make_event(ctx, flags, callback, sock, newconn);
599     return 0;
600 }
601 
602 static void process_packet(verto_ctx *ctx, verto_ev *ev);
603 static void accept_stream_connection(verto_ctx *ctx, verto_ev *ev);
604 static void process_stream_connection_read(verto_ctx *ctx, verto_ev *ev);
605 static void process_stream_connection_write(verto_ctx *ctx, verto_ev *ev);
606 static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);
607 static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);
608 
609 /*
610  * Create a socket and bind it to addr.  Ensure the socket will work with
611  * select().  Set the socket cloexec, reuseaddr, and if applicable v6-only.
612  * Does not call listen().  On failure, log an error and return an error code.
613  */
614 static krb5_error_code
create_server_socket(struct sockaddr * addr,int type,const char * prog,int * fd_out)615 create_server_socket(struct sockaddr *addr, int type, const char *prog,
616                      int *fd_out)
617 {
618     int sock, e;
619     char addrbuf[128];
620 
621     *fd_out = -1;
622 
623     if (addr->sa_family == AF_UNIX)
624         (void)unlink(sa2sun(addr)->sun_path);
625     sock = socket(addr->sa_family, type, 0);
626     if (sock == -1) {
627         e = errno;
628         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
629         com_err(prog, e, _("Cannot create TCP server socket on %s"), addrbuf);
630         return e;
631     }
632     set_cloexec_fd(sock);
633 
634 #ifndef _WIN32                  /* Windows FD_SETSIZE is a count. */
635     if (sock >= FD_SETSIZE) {
636         close(sock);
637         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
638         com_err(prog, 0, _("TCP socket fd number %d (for %s) too high"),
639                 sock, addrbuf);
640         return EMFILE;
641     }
642 #endif
643 
644     if (setreuseaddr(sock, 1) < 0)
645         com_err(prog, errno, _("Cannot enable SO_REUSEADDR on fd %d"), sock);
646 
647     if (addr->sa_family == AF_INET6) {
648 #ifdef IPV6_V6ONLY
649         if (setv6only(sock, 1)) {
650             com_err(prog, errno, _("setsockopt(%d,IPV6_V6ONLY,1) failed"),
651                     sock);
652         } else {
653             com_err(prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"), sock);
654         }
655 #else
656         krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support"));
657 #endif /* IPV6_V6ONLY */
658     }
659 
660     if (bind(sock, addr, sa_socklen(addr)) == -1) {
661         e = errno;
662         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
663         com_err(prog, e, _("Cannot bind server socket on %s"), addrbuf);
664         close(sock);
665         return e;
666     }
667 
668     *fd_out = sock;
669     return 0;
670 }
671 
672 static const int one = 1;
673 
674 static int
setnbio(int sock)675 setnbio(int sock)
676 {
677     return ioctlsocket(sock, FIONBIO, (const void *)&one);
678 }
679 
680 static int
setkeepalive(int sock)681 setkeepalive(int sock)
682 {
683     return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
684 }
685 
686 static int
setnolinger(int s)687 setnolinger(int s)
688 {
689     static const struct linger ling = { 0, 0 };
690     return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
691 }
692 
693 /* An enum map to socket families for each bind_type. */
694 static const int bind_socktypes[] =
695 {
696     [UDP] = SOCK_DGRAM,
697     [TCP] = SOCK_STREAM,
698     [RPC] = SOCK_STREAM,
699     [UNX] = SOCK_STREAM
700 };
701 
702 /* An enum map containing conn_type (for struct connection) for each
703  * bind_type.  */
704 static const enum conn_type bind_conn_types[] =
705 {
706     [UDP] = CONN_UDP,
707     [TCP] = CONN_TCP_LISTENER,
708     [RPC] = CONN_RPC_LISTENER,
709     [UNX] = CONN_UNIXSOCK_LISTENER
710 };
711 
712 /* If any systemd socket activation fds are indicated by the environment, set
713  * them close-on-exec and put their addresses and socket types into *list. */
714 static void
init_sockact_list(struct sockact_list * list)715 init_sockact_list(struct sockact_list *list)
716 {
717     const char *v;
718     char *end;
719     long lpid;
720     int fd;
721     size_t nfds, i;
722     socklen_t slen;
723 
724     list->nsockets = 0;
725     list->fds = NULL;
726 
727     /* Check if LISTEN_FDS is meant for this process. */
728     v = getenv("LISTEN_PID");
729     if (v == NULL)
730         return;
731     lpid = strtol(v, &end, 10);
732     if (end == NULL || end == v || *end != '\0' || lpid != getpid())
733         return;
734 
735     /* Get the number of activated sockets. */
736     v = getenv("LISTEN_FDS");
737     if (v == NULL)
738         return;
739     nfds = strtoul(v, &end, 10);
740     if (end == NULL || end == v || *end != '\0')
741         return;
742     if (nfds == 0 || nfds > (size_t)INT_MAX - SOCKACT_START)
743         return;
744 
745     list->fds = calloc(nfds, sizeof(*list->fds));
746     if (list->fds == NULL)
747         return;
748 
749     for (i = 0; i < nfds; i++) {
750         fd = i + SOCKACT_START;
751         set_cloexec_fd(fd);
752         slen = sizeof(list->fds[i].addr);
753         (void)getsockname(fd, ss2sa(&list->fds[i].addr), &slen);
754         slen = sizeof(list->fds[i].type);
755         (void)getsockopt(fd, SOL_SOCKET, SO_TYPE, &list->fds[i].type, &slen);
756     }
757 
758     list->nsockets = nfds;
759 }
760 
761 /* Release any storage used by *list. */
762 static void
fini_sockact_list(struct sockact_list * list)763 fini_sockact_list(struct sockact_list *list)
764 {
765     free(list->fds);
766     list->fds = NULL;
767     list->nsockets = 0;
768 }
769 
770 /* If sa matches an address in *list, return the associated file descriptor and
771  * clear the address from *list.  Otherwise return -1. */
772 static int
find_sockact(struct sockact_list * list,const struct sockaddr * sa,int type)773 find_sockact(struct sockact_list *list, const struct sockaddr *sa, int type)
774 {
775     size_t i;
776 
777     for (i = 0; i < list->nsockets; i++) {
778         if (list->fds[i].type == type &&
779             sa_equal(ss2sa(&list->fds[i].addr), sa)) {
780             list->fds[i].type = -1;
781             memset(&list->fds[i].addr, 0, sizeof(list->fds[i].addr));
782             return i + SOCKACT_START;
783         }
784     }
785     return -1;
786 }
787 
788 /*
789  * Set up a listening socket.
790  *
791  * Arguments:
792  *
793  * - ba
794  *      The bind address and port for the socket.
795  * - ai
796  *      The addrinfo struct to use for creating the socket.
797  * - ctype
798  *      The conn_type of this socket.
799  */
800 static krb5_error_code
setup_socket(struct bind_address * ba,struct sockaddr * sock_address,struct sockact_list * sockacts,void * handle,const char * prog,verto_ctx * ctx,int listen_backlog,verto_callback vcb,enum conn_type ctype)801 setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
802              struct sockact_list *sockacts, void *handle, const char *prog,
803              verto_ctx *ctx, int listen_backlog, verto_callback vcb,
804              enum conn_type ctype)
805 {
806     krb5_error_code ret;
807     struct connection *conn;
808     verto_ev_flag flags;
809     verto_ev *ev = NULL;
810     int sock = -1;
811     char addrbuf[128];
812 
813     k5_print_addr_port(sock_address, addrbuf, sizeof(addrbuf));
814     krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"),
815                      bind_type_names[ba->type], addrbuf);
816 
817     if (sockacts->nsockets > 0) {
818         /* Look for a systemd socket activation fd matching sock_address. */
819         sock = find_sockact(sockacts, sock_address, bind_socktypes[ba->type]);
820         if (sock == -1) {
821             /* Ignore configured addresses that don't match any caller-provided
822              * sockets. */
823             ret = 0;
824             goto cleanup;
825         }
826     } else {
827         /* We're not using socket activation; create the socket. */
828         ret = create_server_socket(sock_address, bind_socktypes[ba->type],
829                                    prog, &sock);
830         if (ret)
831             goto cleanup;
832 
833         /* Listen for backlogged connections on stream sockets.  (For RPC
834          * sockets this will be done by svc_register().) */
835         if ((ba->type == TCP || ba->type == UNX) &&
836             listen(sock, listen_backlog) != 0) {
837             ret = errno;
838             com_err(prog, errno, _("Cannot listen on %s server socket on %s"),
839                     bind_type_names[ba->type], addrbuf);
840             goto cleanup;
841         }
842     }
843 
844     /* Set non-blocking I/O for non-RPC listener sockets. */
845     if (ba->type != RPC && setnbio(sock) != 0) {
846         ret = errno;
847         com_err(prog, errno,
848                 _("cannot set listening %s socket on %s non-blocking"),
849                 bind_type_names[ba->type], addrbuf);
850         goto cleanup;
851     }
852 
853     /* Turn off the linger option for TCP sockets. */
854     if (ba->type == TCP && setnolinger(sock) != 0) {
855         ret = errno;
856         com_err(prog, errno, _("cannot set SO_LINGER on %s socket on %s"),
857                 bind_type_names[ba->type], addrbuf);
858         goto cleanup;
859     }
860 
861     /* Try to turn on pktinfo for UDP wildcard sockets. */
862     if (ba->type == UDP && sa_is_wildcard(sock_address)) {
863         krb5_klog_syslog(LOG_DEBUG, _("Setting pktinfo on socket %s"),
864                          addrbuf);
865         ret = set_pktinfo(sock, sock_address->sa_family);
866         if (ret) {
867             com_err(prog, ret,
868                     _("Cannot request packet info for UDP socket address "
869                       "%s port %d"), addrbuf, ba->port);
870             krb5_klog_syslog(LOG_INFO, _("System does not support pktinfo yet "
871                                          "binding to a wildcard address.  "
872                                          "Packets are not guaranteed to "
873                                          "return on the received address."));
874         }
875     }
876 
877     /* Add the socket to the event loop. */
878     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST |
879         VERTO_EV_FLAG_REINITIABLE;
880     ret = add_fd(sock, ctype, flags, handle, prog, ctx, vcb, &ev);
881     if (ret) {
882         krb5_klog_syslog(LOG_ERR, _("Error attempting to add verto event"));
883         goto cleanup;
884     }
885 
886     if (ba->type == RPC) {
887         conn = verto_get_private(ev);
888         conn->transp = svctcp_create(sock, 0, 0);
889         if (conn->transp == NULL) {
890             ret = errno;
891             krb5_klog_syslog(LOG_ERR, _("Cannot create RPC service: %s"),
892                              strerror(ret));
893             goto cleanup;
894         }
895 
896         ret = svc_register(conn->transp, ba->rpc_svc_data.prognum,
897                            ba->rpc_svc_data.versnum, ba->rpc_svc_data.dispatch,
898                            0);
899         if (!ret) {
900             ret = errno;
901             krb5_klog_syslog(LOG_ERR, _("Cannot register RPC service: %s"),
902                              strerror(ret));
903             goto cleanup;
904         }
905     }
906 
907     ev = NULL;
908     sock = -1;
909     ret = 0;
910 
911 cleanup:
912     if (sock >= 0)
913         close(sock);
914     if (ev != NULL)
915         verto_del(ev);
916     return ret;
917 }
918 
919 /*
920  * Setup all the socket addresses that the net-server should listen to.
921  *
922  * This function uses getaddrinfo to figure out all the addresses. This will
923  * automatically figure out which socket families that should be used on the
924  * host making it useful even for wildcard addresses.
925  */
926 static krb5_error_code
setup_addresses(verto_ctx * ctx,void * handle,const char * prog,int listen_backlog)927 setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
928                 int listen_backlog)
929 {
930     /* An bind_type enum map for the verto callback functions. */
931     static verto_callback *const verto_callbacks[] = {
932         [UDP] = &process_packet,
933         [TCP] = &accept_stream_connection,
934         [RPC] = &accept_rpc_connection,
935         [UNX] = &accept_stream_connection
936     };
937     krb5_error_code ret = 0;
938     size_t i;
939     int err, bound_any;
940     struct bind_address addr;
941     struct sockaddr_un sun;
942     struct addrinfo hints, *ai_list = NULL, *ai = NULL;
943     struct sockact_list sockacts = { 0 };
944     verto_callback vcb;
945     char addrbuf[128];
946 
947     /* Check to make sure addresses were added to the server. */
948     if (bind_addresses.n == 0) {
949         krb5_klog_syslog(LOG_ERR, _("No addresses added to the net server"));
950         return EINVAL;
951     }
952 
953     /* Ask for all address families, listener addresses, and no port name
954      * resolution. */
955     memset(&hints, 0, sizeof(struct addrinfo));
956     hints.ai_family = AF_UNSPEC;
957     hints.ai_flags = AI_PASSIVE;
958 #ifdef AI_NUMERICSERV
959     hints.ai_flags |= AI_NUMERICSERV;
960 #endif
961 
962     init_sockact_list(&sockacts);
963 
964     /* Add all the requested addresses. */
965     for (i = 0; i < bind_addresses.n; i++) {
966         addr = bind_addresses.data[i];
967         hints.ai_socktype = bind_socktypes[addr.type];
968 
969         if (addr.type == UNX) {
970             sun.sun_family = AF_UNIX;
971             if (strlcpy(sun.sun_path, addr.address, sizeof(sun.sun_path)) >=
972                 sizeof(sun.sun_path)) {
973                 ret = ENAMETOOLONG;
974                 krb5_klog_syslog(LOG_ERR,
975                                  _("UNIX domain socket path too long: %s"),
976                                  addr.address);
977                 goto cleanup;
978             }
979             ret = setup_socket(&addr, (struct sockaddr *)&sun, &sockacts,
980                                handle, prog, ctx, listen_backlog,
981                                verto_callbacks[addr.type],
982                                bind_conn_types[addr.type]);
983             if (ret) {
984                 krb5_klog_syslog(LOG_ERR,
985                                  _("Failed setting up a UNIX socket (for %s)"),
986                                  addr.address);
987                 goto cleanup;
988             }
989             continue;
990         }
991 
992         /* Call getaddrinfo, using a dummy port value. */
993         err = getaddrinfo(addr.address, "0", &hints, &ai_list);
994         if (err) {
995             krb5_klog_syslog(LOG_ERR,
996                              _("Failed getting address info (for %s): %s"),
997                              (addr.address == NULL) ? "<wildcard>" :
998                              addr.address, gai_strerror(err));
999             ret = EIO;
1000             goto cleanup;
1001         }
1002 
1003         /*
1004          * Loop through all the sockets that getaddrinfo could find to match
1005          * the requested address.  For wildcard listeners, this should usually
1006          * have two results, one for each of IPv4 and IPv6, or one or the
1007          * other, depending on the system.  On IPv4-only systems, getaddrinfo()
1008          * may return both IPv4 and IPv6 addresses, but creating an IPv6 socket
1009          * may give an EAFNOSUPPORT error, so tolerate that error as long as we
1010          * can bind at least one socket.
1011          */
1012         bound_any = 0;
1013         for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
1014             /* Make sure getaddrinfo returned a socket with the same type that
1015              * was requested. */
1016             assert(hints.ai_socktype == ai->ai_socktype);
1017 
1018             /* Set the real port number. */
1019             sa_setport(ai->ai_addr, addr.port);
1020 
1021             ret = setup_socket(&addr, ai->ai_addr, &sockacts, handle, prog,
1022                                ctx, listen_backlog, verto_callbacks[addr.type],
1023                                bind_conn_types[addr.type]);
1024             if (ret) {
1025                 k5_print_addr(ai->ai_addr, addrbuf, sizeof(addrbuf));
1026                 krb5_klog_syslog(LOG_ERR,
1027                                  _("Failed setting up a %s socket (for %s)"),
1028                                  bind_type_names[addr.type], addrbuf);
1029                 if (ret != EAFNOSUPPORT)
1030                     goto cleanup;
1031             } else {
1032                 bound_any = 1;
1033             }
1034         }
1035         if (!bound_any)
1036             goto cleanup;
1037         ret = 0;
1038 
1039         if (ai_list != NULL)
1040             freeaddrinfo(ai_list);
1041         ai_list = NULL;
1042     }
1043 
1044 cleanup:
1045     if (ai_list != NULL)
1046         freeaddrinfo(ai_list);
1047     fini_sockact_list(&sockacts);
1048     return ret;
1049 }
1050 
1051 krb5_error_code
loop_setup_network(verto_ctx * ctx,void * handle,const char * prog,int listen_backlog)1052 loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,
1053                    int listen_backlog)
1054 {
1055     krb5_error_code ret;
1056     verto_ev *ev;
1057     int i;
1058 
1059     /* Check to make sure that at least one address was added to the loop. */
1060     if (bind_addresses.n == 0)
1061         return EINVAL;
1062 
1063     /* Close any open connections. */
1064     FOREACH_ELT(events, i, ev)
1065         verto_del(ev);
1066     events.n = 0;
1067 
1068     krb5_klog_syslog(LOG_INFO, _("setting up network..."));
1069     ret = setup_addresses(ctx, handle, prog, listen_backlog);
1070     if (ret) {
1071         com_err(prog, ret, _("Error setting up network"));
1072         exit(1);
1073     }
1074     krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);
1075     if (events.n == 0) {
1076         /* If no sockets were set up, we can't continue. */
1077         com_err(prog, 0, _("no sockets set up?"));
1078         exit (1);
1079     }
1080 
1081     return 0;
1082 }
1083 
1084 struct udp_dispatch_state {
1085     void *handle;
1086     const char *prog;
1087     int port_fd;
1088     struct sockaddr_storage saddr;
1089     struct sockaddr_storage daddr;
1090     aux_addressing_info auxaddr;
1091     krb5_data request;
1092     char pktbuf[MAX_DGRAM_SIZE];
1093 };
1094 
1095 static void
process_packet_response(void * arg,krb5_error_code code,krb5_data * response)1096 process_packet_response(void *arg, krb5_error_code code, krb5_data *response)
1097 {
1098     struct udp_dispatch_state *state = arg;
1099     int cc;
1100 
1101     if (code)
1102         com_err(state->prog ? state->prog : NULL, code,
1103                 _("while dispatching (udp)"));
1104     if (code || response == NULL)
1105         goto out;
1106 
1107     cc = send_to_from(state->port_fd, response->data,
1108                       (socklen_t)response->length, 0, ss2sa(&state->saddr),
1109                       ss2sa(&state->daddr), &state->auxaddr);
1110     if (cc == -1) {
1111         /* Note that the local address (daddr*) has no port number
1112          * info associated with it. */
1113         char sbuf[128], dbuf[128];
1114         int e = errno;
1115 
1116         k5_print_addr_port(ss2sa(&state->saddr), sbuf, sizeof(sbuf));
1117         k5_print_addr(ss2sa(&state->daddr), dbuf, sizeof(dbuf));
1118         com_err(state->prog, e, _("while sending reply to %s from %s"),
1119                 sbuf, dbuf);
1120         goto out;
1121     }
1122     if ((size_t)cc != response->length) {
1123         com_err(state->prog, 0, _("short reply write %d vs %d\n"),
1124                 response->length, cc);
1125     }
1126 
1127 out:
1128     krb5_free_data(get_context(state->handle), response);
1129     free(state);
1130 }
1131 
1132 static void
process_packet(verto_ctx * ctx,verto_ev * ev)1133 process_packet(verto_ctx *ctx, verto_ev *ev)
1134 {
1135     int cc;
1136     struct connection *conn;
1137     struct udp_dispatch_state *state;
1138     socklen_t slen;
1139 
1140     conn = verto_get_private(ev);
1141 
1142     state = malloc(sizeof(*state));
1143     if (!state) {
1144         com_err(conn->prog, ENOMEM, _("while dispatching (udp)"));
1145         return;
1146     }
1147 
1148     state->handle = conn->handle;
1149     state->prog = conn->prog;
1150     state->port_fd = verto_get_fd(ev);
1151     assert(state->port_fd >= 0);
1152 
1153     memset(&state->auxaddr, 0, sizeof(state->auxaddr));
1154     cc = recv_from_to(state->port_fd, state->pktbuf, sizeof(state->pktbuf), 0,
1155                       &state->saddr, &state->daddr, &state->auxaddr);
1156     if (cc == -1) {
1157         if (errno != EINTR && errno != EAGAIN
1158             /*
1159              * This is how Linux indicates that a previous transmission was
1160              * refused, e.g., if the client timed out before getting the
1161              * response packet.
1162              */
1163             && errno != ECONNREFUSED
1164         )
1165             com_err(conn->prog, errno, _("while receiving from network"));
1166         free(state);
1167         return;
1168     }
1169     if (!cc) { /* zero-length packet? */
1170         free(state);
1171         return;
1172     }
1173 
1174     if (state->daddr.ss_family == AF_UNSPEC && conn->type == CONN_UDP) {
1175         /*
1176          * An address couldn't be obtained, so the PKTINFO option probably
1177          * isn't available.  If the socket is bound to a specific address, then
1178          * try to get the address here.
1179          */
1180         slen = sizeof(state->daddr);
1181         (void)getsockname(state->port_fd, ss2sa(&state->daddr), &slen);
1182     }
1183 
1184     state->request.length = cc;
1185     state->request.data = state->pktbuf;
1186 
1187     dispatch(state->handle, ss2sa(&state->daddr), ss2sa(&state->saddr),
1188              &state->request, 0, ctx, process_packet_response, state);
1189 }
1190 
1191 static int
kill_lru_stream_connection(void * handle,verto_ev * newev)1192 kill_lru_stream_connection(void *handle, verto_ev *newev)
1193 {
1194     struct connection *c = NULL, *oldest_c = NULL;
1195     verto_ev *ev, *oldest_ev = NULL;
1196     int i, fd = -1;
1197 
1198     krb5_klog_syslog(LOG_INFO, _("too many connections"));
1199 
1200     FOREACH_ELT (events, i, ev) {
1201         if (ev == newev)
1202             continue;
1203 
1204         c = verto_get_private(ev);
1205         if (!c)
1206             continue;
1207         if (c->type != CONN_TCP && c->type != CONN_RPC &&
1208             c->type != CONN_UNIXSOCK)
1209             continue;
1210         if (oldest_c == NULL
1211             || oldest_c->start_time > c->start_time) {
1212             oldest_ev = ev;
1213             oldest_c = c;
1214         }
1215     }
1216     if (oldest_c != NULL) {
1217         krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
1218                          conn_type_names[oldest_c->type],
1219                          verto_get_fd(oldest_ev), oldest_c->addrbuf);
1220         if (oldest_c->type == CONN_RPC)
1221             oldest_c->rpc_force_close = 1;
1222         verto_del(oldest_ev);
1223     }
1224     return fd;
1225 }
1226 
1227 static void
accept_stream_connection(verto_ctx * ctx,verto_ev * ev)1228 accept_stream_connection(verto_ctx *ctx, verto_ev *ev)
1229 {
1230     int s;
1231     struct sockaddr_storage addr;
1232     socklen_t addrlen = sizeof(addr);
1233     struct connection *newconn, *conn;
1234     enum conn_type ctype;
1235     verto_ev_flag flags;
1236     verto_ev *newev;
1237 
1238     conn = verto_get_private(ev);
1239     s = accept(verto_get_fd(ev), ss2sa(&addr), &addrlen);
1240     if (s < 0)
1241         return;
1242     set_cloexec_fd(s);
1243 #ifndef _WIN32
1244     if (s >= FD_SETSIZE) {
1245         close(s);
1246         return;
1247     }
1248 #endif
1249     setnbio(s);
1250     setnolinger(s);
1251     if (addr.ss_family != AF_UNIX)
1252         setkeepalive(s);
1253 
1254     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1255     ctype = (conn->type == CONN_TCP_LISTENER) ? CONN_TCP : CONN_UNIXSOCK;
1256     if (add_fd(s, ctype, flags, conn->handle, conn->prog, ctx,
1257                process_stream_connection_read, &newev) != 0) {
1258         close(s);
1259         return;
1260     }
1261     newconn = verto_get_private(newev);
1262 
1263     if (addr.ss_family == AF_UNIX) {
1264         /* accept() doesn't fill in sun_path as the client socket isn't bound.
1265          * For logging purposes we will use the target address. */
1266         addrlen = sizeof(addr);
1267         if (getsockname(s, ss2sa(&addr), &addrlen) < 0) {
1268             com_err(conn->prog, errno, _("Failed to get address for %d"), s);
1269             close(s);
1270             return;
1271         }
1272     }
1273 
1274     k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,
1275                        sizeof(newconn->addrbuf));
1276     newconn->addr_s = addr;
1277     newconn->addrlen = addrlen;
1278     newconn->bufsiz = 1024 * 1024;
1279     newconn->buffer = malloc(newconn->bufsiz);
1280     newconn->start_time = time(0);
1281 
1282     if (++stream_data_counter > max_stream_data_connections)
1283         kill_lru_stream_connection(conn->handle, newev);
1284 
1285     if (newconn->buffer == 0) {
1286         com_err(conn->prog, errno,
1287                 _("allocating buffer for new TCP session from %s"),
1288                 newconn->addrbuf);
1289         verto_del(newev);
1290         return;
1291     }
1292     newconn->offset = 0;
1293     SG_SET(&newconn->sgbuf[0], newconn->lenbuf, 4);
1294     SG_SET(&newconn->sgbuf[1], 0, 0);
1295 }
1296 
1297 struct tcp_dispatch_state {
1298     struct sockaddr_storage local_saddr;
1299     struct connection *conn;
1300     krb5_data request;
1301     verto_ctx *ctx;
1302     int sock;
1303 };
1304 
1305 static void
process_stream_response(void * arg,krb5_error_code code,krb5_data * response)1306 process_stream_response(void *arg, krb5_error_code code, krb5_data *response)
1307 {
1308     struct tcp_dispatch_state *state = arg;
1309     verto_ev *ev;
1310 
1311     assert(state);
1312     state->conn->response = response;
1313 
1314     if (code)
1315         com_err(state->conn->prog, code, _("while dispatching (tcp)"));
1316     if (code || !response)
1317         goto kill_tcp_connection;
1318 
1319     /* Queue outgoing response. */
1320     store_32_be(response->length, state->conn->lenbuf);
1321     SG_SET(&state->conn->sgbuf[1], response->data, response->length);
1322     state->conn->sgp = state->conn->sgbuf;
1323     state->conn->sgnum = 2;
1324 
1325     ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,
1326                     process_stream_connection_write, state->sock, state->conn);
1327     if (ev) {
1328         free(state);
1329         return;
1330     }
1331 
1332 kill_tcp_connection:
1333     stream_data_counter--;
1334     free_connection(state->conn);
1335     close(state->sock);
1336     free(state);
1337 }
1338 
1339 /* Creates the tcp_dispatch_state and deletes the verto event. */
1340 static struct tcp_dispatch_state *
prepare_for_dispatch(verto_ctx * ctx,verto_ev * ev)1341 prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)
1342 {
1343     struct tcp_dispatch_state *state;
1344 
1345     state = malloc(sizeof(*state));
1346     if (!state) {
1347         krb5_klog_syslog(LOG_ERR, _("error allocating tcp dispatch private!"));
1348         return NULL;
1349     }
1350     state->conn = verto_get_private(ev);
1351     state->sock = verto_get_fd(ev);
1352     state->ctx = ctx;
1353     verto_set_private(ev, NULL, NULL); /* Don't close the fd or free conn! */
1354     remove_event_from_set(ev); /* Remove it from the set. */
1355     verto_del(ev);
1356     return state;
1357 }
1358 
1359 static void
process_stream_connection_read(verto_ctx * ctx,verto_ev * ev)1360 process_stream_connection_read(verto_ctx *ctx, verto_ev *ev)
1361 {
1362     struct tcp_dispatch_state *state = NULL;
1363     struct connection *conn = NULL;
1364     ssize_t nread;
1365     size_t len;
1366 
1367     conn = verto_get_private(ev);
1368 
1369     /*
1370      * Read message length and data into one big buffer, already allocated
1371      * at connect time.  If we have a complete message, we stop reading, so
1372      * we should only be here if there is no data in the buffer, or only an
1373      * incomplete message.
1374      */
1375     if (conn->offset < 4) {
1376         krb5_data *response = NULL;
1377 
1378         /* msglen has not been computed.  XXX Doing at least two reads
1379          * here, letting the kernel worry about buffering. */
1380         len = 4 - conn->offset;
1381         nread = SOCKET_READ(verto_get_fd(ev),
1382                             conn->buffer + conn->offset, len);
1383         if (nread < 0) /* error */
1384             goto kill_tcp_connection;
1385         if (nread == 0) /* eof */
1386             goto kill_tcp_connection;
1387         conn->offset += nread;
1388         if (conn->offset == 4) {
1389             unsigned char *p = (unsigned char *)conn->buffer;
1390             conn->msglen = load_32_be(p);
1391             if (conn->msglen > conn->bufsiz - 4) {
1392                 krb5_error_code err;
1393                 /* Message too big. */
1394                 krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, "
1395                                             "cap is %lu"), conn->addrbuf,
1396                                  (unsigned long) conn->msglen,
1397                                  (unsigned long) conn->bufsiz - 4);
1398                 /* XXX Should return an error.  */
1399                 err = make_toolong_error (conn->handle,
1400                                           &response);
1401                 if (err) {
1402                     krb5_klog_syslog(LOG_ERR, _("error constructing "
1403                                                 "KRB_ERR_FIELD_TOOLONG error! %s"),
1404                                      error_message(err));
1405                     goto kill_tcp_connection;
1406                 }
1407 
1408                 state = prepare_for_dispatch(ctx, ev);
1409                 if (!state) {
1410                     krb5_free_data(get_context(conn->handle), response);
1411                     goto kill_tcp_connection;
1412                 }
1413                 process_stream_response(state, 0, response);
1414             }
1415         }
1416     } else {
1417         /* msglen known. */
1418         socklen_t local_saddrlen = sizeof(struct sockaddr_storage);
1419 
1420         len = conn->msglen - (conn->offset - 4);
1421         nread = SOCKET_READ(verto_get_fd(ev),
1422                             conn->buffer + conn->offset, len);
1423         if (nread < 0) /* error */
1424             goto kill_tcp_connection;
1425         if (nread == 0) /* eof */
1426             goto kill_tcp_connection;
1427         conn->offset += nread;
1428         if (conn->offset < conn->msglen + 4)
1429             return;
1430 
1431         /* Have a complete message, and exactly one message. */
1432         state = prepare_for_dispatch(ctx, ev);
1433         if (!state)
1434             goto kill_tcp_connection;
1435 
1436         state->request.length = conn->msglen;
1437         state->request.data = conn->buffer + 4;
1438 
1439         if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr),
1440                         &local_saddrlen) < 0) {
1441             krb5_klog_syslog(LOG_ERR, _("getsockname failed: %s"),
1442                              error_message(errno));
1443             goto kill_tcp_connection;
1444         }
1445         dispatch(state->conn->handle, ss2sa(&state->local_saddr),
1446                  ss2sa(&conn->addr_s), &state->request, 1, ctx,
1447                  process_stream_response, state);
1448     }
1449 
1450     return;
1451 
1452 kill_tcp_connection:
1453     verto_del(ev);
1454 }
1455 
1456 static void
process_stream_connection_write(verto_ctx * ctx,verto_ev * ev)1457 process_stream_connection_write(verto_ctx *ctx, verto_ev *ev)
1458 {
1459     struct connection *conn;
1460     SOCKET_WRITEV_TEMP tmp;
1461     ssize_t nwrote;
1462     int sock;
1463 
1464     conn = verto_get_private(ev);
1465     sock = verto_get_fd(ev);
1466 
1467     nwrote = SOCKET_WRITEV(sock, conn->sgp,
1468                            conn->sgnum, tmp);
1469     if (nwrote > 0) { /* non-error and non-eof */
1470         while (nwrote) {
1471             sg_buf *sgp = conn->sgp;
1472             if ((size_t)nwrote < SG_LEN(sgp)) {
1473                 SG_ADVANCE(sgp, (size_t)nwrote);
1474                 nwrote = 0;
1475             } else {
1476                 nwrote -= SG_LEN(sgp);
1477                 conn->sgp++;
1478                 conn->sgnum--;
1479                 if (conn->sgnum == 0 && nwrote != 0)
1480                     abort();
1481             }
1482         }
1483 
1484         /* If we still have more data to send, just return so that
1485          * the main loop can call this function again when the socket
1486          * is ready for more writing. */
1487         if (conn->sgnum > 0)
1488             return;
1489     }
1490 
1491     /* Finished sending.  We should go back to reading, though if we
1492      * sent a FIELD_TOOLONG error in reply to a length with the high
1493      * bit set, RFC 4120 says we have to close the TCP stream. */
1494     verto_del(ev);
1495 }
1496 
1497 void
loop_free(verto_ctx * ctx)1498 loop_free(verto_ctx *ctx)
1499 {
1500     int i;
1501     struct bind_address val;
1502 
1503     verto_free(ctx);
1504 
1505     /* Free each addresses added to the loop. */
1506     FOREACH_ELT(bind_addresses, i, val)
1507         free(val.address);
1508     FREE_SET_DATA(bind_addresses);
1509     FREE_SET_DATA(events);
1510 }
1511 
1512 static int
have_event_for_fd(int fd)1513 have_event_for_fd(int fd)
1514 {
1515     verto_ev *ev;
1516     int i;
1517 
1518     FOREACH_ELT(events, i, ev) {
1519         if (verto_get_fd(ev) == fd)
1520             return 1;
1521     }
1522 
1523     return 0;
1524 }
1525 
1526 static void
accept_rpc_connection(verto_ctx * ctx,verto_ev * ev)1527 accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
1528 {
1529     verto_ev_flag flags;
1530     struct connection *conn;
1531     fd_set fds;
1532     int s;
1533 
1534     conn = verto_get_private(ev);
1535 
1536     /* Service the woken RPC listener descriptor. */
1537     FD_ZERO(&fds);
1538     FD_SET(verto_get_fd(ev), &fds);
1539     svc_getreqset(&fds);
1540 
1541     /* Scan svc_fdset for any new connections. */
1542     for (s = 0; s < FD_SETSIZE; s++) {
1543         struct sockaddr_storage addr;
1544         socklen_t addrlen = sizeof(addr);
1545         struct connection *newconn;
1546         verto_ev *newev;
1547 
1548         /* If we already have this fd, continue. */
1549         if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s))
1550             continue;
1551 
1552         flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1553         if (add_fd(s, CONN_RPC, flags, conn->handle, conn->prog, ctx,
1554                    process_rpc_connection, &newev) != 0)
1555             continue;
1556         newconn = verto_get_private(newev);
1557 
1558         set_cloexec_fd(s);
1559 
1560         if (getpeername(s, ss2sa(&addr), &addrlen) != 0) {
1561             strlcpy(newconn->addrbuf, "<unknown>", sizeof(newconn->addrbuf));
1562         } else {
1563             k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,
1564                                sizeof(newconn->addrbuf));
1565         }
1566 
1567         newconn->addr_s = addr;
1568         newconn->addrlen = addrlen;
1569         newconn->start_time = time(0);
1570 
1571         if (++stream_data_counter > max_stream_data_connections)
1572             kill_lru_stream_connection(newconn->handle, newev);
1573     }
1574 }
1575 
1576 static void
process_rpc_connection(verto_ctx * ctx,verto_ev * ev)1577 process_rpc_connection(verto_ctx *ctx, verto_ev *ev)
1578 {
1579     fd_set fds;
1580 
1581     FD_ZERO(&fds);
1582     FD_SET(verto_get_fd(ev), &fds);
1583     svc_getreqset(&fds);
1584 
1585     if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))
1586         verto_del(ev);
1587 }
1588 
1589 #endif /* INET */
1590