Path: blob/main/crypto/krb5/src/lib/apputils/net-server.c
39562 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */2/*3* Copyright 1990,2000,2007,2008,2009,2010,2016 by the Massachusetts Institute4* of Technology.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/2526#include "k5-int.h"27#include "adm_proto.h"28#include <sys/ioctl.h>29#include <syslog.h>3031#include <stddef.h>32#include "port-sockets.h"33#include "socket-utils.h"3435#include <gssrpc/rpc.h>3637#ifdef HAVE_NETINET_IN_H38#include <sys/types.h>39#include <netinet/in.h>40#include <sys/socket.h>41#include <sys/un.h>42#ifdef HAVE_SYS_SOCKIO_H43/* for SIOCGIFCONF, etc. */44#include <sys/sockio.h>45#endif46#include <sys/time.h>47#if HAVE_SYS_SELECT_H48#include <sys/select.h>49#endif50#include <arpa/inet.h>5152#ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */53#include <net/if.h>54#endif5556#ifdef HAVE_SYS_FILIO_H57#include <sys/filio.h> /* FIONBIO */58#endif5960#include "fake-addrinfo.h"61#include "net-server.h"62#include <signal.h>63#include <netdb.h>6465#include "udppktinfo.h"6667/* List of systemd socket activation addresses and socket types. */68struct sockact_list {69size_t nsockets;70struct {71struct sockaddr_storage addr;72int type;73} *fds;74};7576/* When systemd socket activation is used, caller-provided sockets begin at77* file descriptor 3. */78const int SOCKACT_START = 3;7980/* XXX */81#define KDC5_NONET (-1779992062L)8283static int stream_data_counter;84static int max_stream_data_connections = 45;8586static int87setreuseaddr(int sock, int value)88{89int st;9091st = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));92if (st)93return st;94#if defined(SO_REUSEPORT) && defined(__APPLE__)95/* macOS experimentally needs this flag as well to avoid conflicts between96* recently exited server processes and new ones. */97st = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));98if (st)99return st;100#endif101return 0;102}103104#if defined(IPV6_V6ONLY)105static int106setv6only(int sock, int value)107{108return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));109}110#endif111112/* KDC data. */113114enum conn_type {115CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC,116CONN_UNIXSOCK_LISTENER, CONN_UNIXSOCK117};118119static const char *const conn_type_names[] = {120[CONN_UDP] = "UDP",121[CONN_TCP_LISTENER] = "TCP listener",122[CONN_TCP] = "TCP",123[CONN_RPC_LISTENER] = "RPC listener",124[CONN_RPC] = "RPC",125[CONN_UNIXSOCK_LISTENER] = "UNIX domain socket listener",126[CONN_UNIXSOCK] = "UNIX domain socket"127};128129enum bind_type {130UDP, TCP, RPC, UNX131};132133static const char *const bind_type_names[] = {134[UDP] = "UDP",135[TCP] = "TCP",136[RPC] = "RPC",137[UNX] = "UNIXSOCK",138};139140/* Per-connection info. */141struct connection {142void *handle;143const char *prog;144enum conn_type type;145146/* Connection fields (TCP or RPC) */147struct sockaddr_storage addr_s;148socklen_t addrlen;149char addrbuf[128];150151/* Incoming data (TCP) */152size_t bufsiz;153size_t offset;154char *buffer;155size_t msglen;156157/* Outgoing data (TCP) */158krb5_data *response;159unsigned char lenbuf[4];160sg_buf sgbuf[2];161sg_buf *sgp;162int sgnum;163164/* Crude denial-of-service avoidance support (TCP or RPC) */165time_t start_time;166167/* RPC-specific fields */168SVCXPRT *transp;169int rpc_force_close;170};171172#define SET(TYPE) struct { TYPE *data; size_t n, max; }173174/* Start at the top and work down -- this should allow for deletions175without disrupting the iteration, since we delete by overwriting176the element to be removed with the last element. */177#define FOREACH_ELT(set,idx,vvar) \178for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)179180#define GROW_SET(set, incr, tmpptr) \181((set.max + incr < set.max \182|| ((set.max + incr) * sizeof(set.data[0]) / sizeof(set.data[0]) \183!= set.max + incr)) \184? 0 /* overflow */ \185: ((tmpptr = realloc(set.data, \186(set.max + incr) * sizeof(set.data[0]))) \187? (set.data = tmpptr, set.max += incr, 1) \188: 0))189190/* 1 = success, 0 = failure */191#define ADD(set, val, tmpptr) \192((set.n < set.max || GROW_SET(set, 10, tmpptr)) \193? (set.data[set.n++] = val, 1) \194: 0)195196#define DEL(set, idx) \197(set.data[idx] = set.data[--set.n], 0)198199#define FREE_SET_DATA(set) \200(free(set.data), set.data = 0, set.max = 0, set.n = 0)201202/*203* N.B.: The Emacs cc-mode indentation code seems to get confused if204* the macro argument here is one word only. So use "unsigned short"205* instead of the "u_short" we were using before.206*/207struct rpc_svc_data {208u_long prognum;209u_long versnum;210void (*dispatch)(struct svc_req *, SVCXPRT *);211};212213struct bind_address {214char *address;215u_short port;216enum bind_type type;217struct rpc_svc_data rpc_svc_data;218};219220static SET(verto_ev *) events;221static SET(struct bind_address) bind_addresses;222223verto_ctx *224loop_init(verto_ev_type types)225{226types |= VERTO_EV_TYPE_IO;227types |= VERTO_EV_TYPE_SIGNAL;228types |= VERTO_EV_TYPE_TIMEOUT;229return verto_default(NULL, types);230}231232static void233do_break(verto_ctx *ctx, verto_ev *ev)234{235krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));236verto_break(ctx);237}238239struct sighup_context {240void *handle;241void (*reset)(void *);242};243244static void245do_reset(verto_ctx *ctx, verto_ev *ev)246{247struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev);248249krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset"));250krb5_klog_reopen(get_context(sc->handle));251if (sc->reset)252sc->reset(sc->handle);253}254255static void256free_sighup_context(verto_ctx *ctx, verto_ev *ev)257{258free(verto_get_private(ev));259}260261krb5_error_code262loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)(void *))263{264struct sighup_context *sc;265verto_ev *ev;266267if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT) ||268!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) ||269!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) ||270!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE))271return ENOMEM;272273ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP);274if (!ev)275return ENOMEM;276277sc = malloc(sizeof(*sc));278if (!sc)279return ENOMEM;280281sc->handle = handle;282sc->reset = reset;283verto_set_private(ev, sc, free_sighup_context);284return 0;285}286287/*288* Add a bind address to the loop.289*290* Arguments:291* - address292* An address string, hostname, or UNIX socket path.293* Pass NULL to use the wildcard address for IP sockets.294* - port295* What port the socket should be set to (for IPv4 or IPv6).296* - type297* bind_type for the socket.298* - rpc_data299* For RPC addresses, the svc_register() arguments to use when TCP300* connections are created. Ignored for other types.301*/302static krb5_error_code303loop_add_address(const char *address, int port, enum bind_type type,304struct rpc_svc_data *rpc_data)305{306struct bind_address addr, val;307int i;308void *tmp;309char *addr_copy = NULL;310311assert(!(type == RPC && rpc_data == NULL));312313/* Make sure a valid port number was passed. */314if (port < 0 || port > 65535) {315krb5_klog_syslog(LOG_ERR, _("Invalid port %d"), port);316return EINVAL;317}318319/* Check for conflicting addresses. */320FOREACH_ELT(bind_addresses, i, val) {321if (type != val.type || port != val.port)322continue;323324/* If a wildcard address is being added, make sure to remove any direct325* addresses. */326if (address == NULL && val.address != NULL) {327krb5_klog_syslog(LOG_DEBUG,328_("Removing address %s since wildcard address"329" is being added"),330val.address);331free(val.address);332DEL(bind_addresses, i);333} else if (val.address == NULL || !strcmp(address, val.address)) {334krb5_klog_syslog(LOG_DEBUG,335_("Address already added to server"));336return 0;337}338}339340/* Copy the address if it is specified. */341if (address != NULL) {342addr_copy = strdup(address);343if (addr_copy == NULL)344return ENOMEM;345}346347/* Add the new address to bind_addresses. */348memset(&addr, 0, sizeof(addr));349addr.address = addr_copy;350addr.port = port;351addr.type = type;352if (rpc_data != NULL)353addr.rpc_svc_data = *rpc_data;354if (!ADD(bind_addresses, addr, tmp)) {355free(addr_copy);356return ENOMEM;357}358359return 0;360}361362/*363* Add bind addresses to the loop.364*365* Arguments:366*367* - addresses368* A string for the addresses. Pass NULL to use the wildcard address.369* Supported delimiters can be found in ADDRESSES_DELIM. Addresses are370* parsed with k5_parse_host_name().371* - default_port372* What port the socket should be set to if not specified in addresses.373* - type374* bind_type for the socket.375* - rpc_data376* For RPC addresses, the svc_register() arguments to use when TCP377* connections are created. Ignored for other types.378*/379static krb5_error_code380loop_add_addresses(const char *addresses, int default_port,381enum bind_type type, struct rpc_svc_data *rpc_data)382{383krb5_error_code ret = 0;384char *addresses_copy = NULL, *host = NULL, *saveptr, *addr;385int port;386387/* If no addresses are set, add a wildcard address. */388if (addresses == NULL)389return loop_add_address(NULL, default_port, type, rpc_data);390391/* Copy the addresses string before using strtok(). */392addresses_copy = strdup(addresses);393if (addresses_copy == NULL) {394ret = ENOMEM;395goto cleanup;396}397398/* Loop through each address in the string and add it to the loop. */399addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr);400for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) {401if (type == UNX) {402/* Skip non-pathnames when binding UNIX domain sockets. */403if (*addr != '/')404continue;405ret = loop_add_address(addr, 0, type, rpc_data);406if (ret)407goto cleanup;408continue;409} else if (*addr == '/') {410/* Skip pathnames when not binding UNIX domain sockets. */411continue;412}413414/* Parse the host string. */415ret = k5_parse_host_string(addr, default_port, &host, &port);416if (ret)417goto cleanup;418419ret = loop_add_address(host, port, type, rpc_data);420if (ret)421goto cleanup;422423free(host);424host = NULL;425}426427ret = 0;428cleanup:429free(addresses_copy);430free(host);431return ret;432}433434krb5_error_code435loop_add_udp_address(int default_port, const char *addresses)436{437return loop_add_addresses(addresses, default_port, UDP, NULL);438}439440krb5_error_code441loop_add_tcp_address(int default_port, const char *addresses)442{443return loop_add_addresses(addresses, default_port, TCP, NULL);444}445446krb5_error_code447loop_add_rpc_service(int default_port, const char *addresses, u_long prognum,448u_long versnum,449void (*dispatchfn)(struct svc_req *, SVCXPRT *))450{451struct rpc_svc_data svc;452453svc.prognum = prognum;454svc.versnum = versnum;455svc.dispatch = dispatchfn;456return loop_add_addresses(addresses, default_port, RPC, &svc);457}458459krb5_error_code460loop_add_unix_socket(const char *socket_paths)461{462/* There is no wildcard or default UNIX domain socket. */463if (socket_paths == NULL)464return 0;465466return loop_add_addresses(socket_paths, 0, UNX, NULL);467}468469#define USE_AF AF_INET470#define USE_TYPE SOCK_DGRAM471#define USE_PROTO 0472#define SOCKET_ERRNO errno473#include "foreachaddr.h"474475static void476free_connection(struct connection *conn)477{478if (!conn)479return;480if (conn->response)481krb5_free_data(get_context(conn->handle), conn->response);482if (conn->buffer)483free(conn->buffer);484if (conn->type == CONN_RPC_LISTENER && conn->transp != NULL)485svc_destroy(conn->transp);486free(conn);487}488489static void490remove_event_from_set(verto_ev *ev)491{492verto_ev *tmp;493int i;494495/* Remove the event from the events. */496FOREACH_ELT(events, i, tmp)497if (tmp == ev) {498DEL(events, i);499break;500}501}502503static void504free_socket(verto_ctx *ctx, verto_ev *ev)505{506struct connection *conn = NULL;507fd_set fds;508int fd;509510remove_event_from_set(ev);511512fd = verto_get_fd(ev);513conn = verto_get_private(ev);514515/* Close the file descriptor. */516krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd);517if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->rpc_force_close))518close(fd);519520/* Free the connection struct. */521if (conn) {522switch (conn->type) {523case CONN_RPC:524if (conn->rpc_force_close) {525FD_ZERO(&fds);526FD_SET(fd, &fds);527svc_getreqset(&fds);528if (FD_ISSET(fd, &svc_fdset)) {529krb5_klog_syslog(LOG_ERR,530_("descriptor %d closed but still "531"in svc_fdset"),532fd);533}534}535/* Fall through. */536case CONN_TCP:537case CONN_UNIXSOCK:538stream_data_counter--;539break;540default:541break;542}543544free_connection(conn);545}546}547548static verto_ev *549make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback,550int sock, struct connection *conn)551{552verto_ev *ev;553void *tmp;554555ev = verto_add_io(ctx, flags, callback, sock);556if (!ev) {557com_err(conn->prog, ENOMEM, _("cannot create io event"));558return NULL;559}560561if (!ADD(events, ev, tmp)) {562com_err(conn->prog, ENOMEM, _("cannot save event"));563verto_del(ev);564return NULL;565}566567verto_set_private(ev, conn, free_socket);568return ev;569}570571static krb5_error_code572add_fd(int sock, enum conn_type conntype, verto_ev_flag flags, void *handle,573const char *prog, verto_ctx *ctx, verto_callback callback,574verto_ev **ev_out)575{576struct connection *newconn;577578*ev_out = NULL;579580#ifndef _WIN32581if (sock >= FD_SETSIZE) {582com_err(prog, 0, _("file descriptor number %d too high"), sock);583return EMFILE;584}585#endif586newconn = malloc(sizeof(*newconn));587if (newconn == NULL) {588com_err(prog, ENOMEM,589_("cannot allocate storage for connection info"));590return ENOMEM;591}592memset(newconn, 0, sizeof(*newconn));593newconn->handle = handle;594newconn->prog = prog;595newconn->type = conntype;596597*ev_out = make_event(ctx, flags, callback, sock, newconn);598return 0;599}600601static void process_packet(verto_ctx *ctx, verto_ev *ev);602static void accept_stream_connection(verto_ctx *ctx, verto_ev *ev);603static void process_stream_connection_read(verto_ctx *ctx, verto_ev *ev);604static void process_stream_connection_write(verto_ctx *ctx, verto_ev *ev);605static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);606static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);607608/*609* Create a socket and bind it to addr. Ensure the socket will work with610* select(). Set the socket cloexec, reuseaddr, and if applicable v6-only.611* Does not call listen(). On failure, log an error and return an error code.612*/613static krb5_error_code614create_server_socket(struct sockaddr *addr, int type, const char *prog,615int *fd_out)616{617int sock, e;618char addrbuf[128];619620*fd_out = -1;621622if (addr->sa_family == AF_UNIX)623(void)unlink(sa2sun(addr)->sun_path);624sock = socket(addr->sa_family, type, 0);625if (sock == -1) {626e = errno;627k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));628com_err(prog, e, _("Cannot create TCP server socket on %s"), addrbuf);629return e;630}631set_cloexec_fd(sock);632633#ifndef _WIN32 /* Windows FD_SETSIZE is a count. */634if (sock >= FD_SETSIZE) {635close(sock);636k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));637com_err(prog, 0, _("TCP socket fd number %d (for %s) too high"),638sock, addrbuf);639return EMFILE;640}641#endif642643if (setreuseaddr(sock, 1) < 0)644com_err(prog, errno, _("Cannot enable SO_REUSEADDR on fd %d"), sock);645646if (addr->sa_family == AF_INET6) {647#ifdef IPV6_V6ONLY648if (setv6only(sock, 1)) {649com_err(prog, errno, _("setsockopt(%d,IPV6_V6ONLY,1) failed"),650sock);651} else {652com_err(prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"), sock);653}654#else655krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support"));656#endif /* IPV6_V6ONLY */657}658659if (bind(sock, addr, sa_socklen(addr)) == -1) {660e = errno;661k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));662com_err(prog, e, _("Cannot bind server socket on %s"), addrbuf);663close(sock);664return e;665}666667*fd_out = sock;668return 0;669}670671static const int one = 1;672673static int674setnbio(int sock)675{676return ioctlsocket(sock, FIONBIO, (const void *)&one);677}678679static int680setkeepalive(int sock)681{682return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));683}684685static int686setnolinger(int s)687{688static const struct linger ling = { 0, 0 };689return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));690}691692/* An enum map to socket families for each bind_type. */693static const int bind_socktypes[] =694{695[UDP] = SOCK_DGRAM,696[TCP] = SOCK_STREAM,697[RPC] = SOCK_STREAM,698[UNX] = SOCK_STREAM699};700701/* An enum map containing conn_type (for struct connection) for each702* bind_type. */703static const enum conn_type bind_conn_types[] =704{705[UDP] = CONN_UDP,706[TCP] = CONN_TCP_LISTENER,707[RPC] = CONN_RPC_LISTENER,708[UNX] = CONN_UNIXSOCK_LISTENER709};710711/* If any systemd socket activation fds are indicated by the environment, set712* them close-on-exec and put their addresses and socket types into *list. */713static void714init_sockact_list(struct sockact_list *list)715{716const char *v;717char *end;718long lpid;719int fd;720size_t nfds, i;721socklen_t slen;722723list->nsockets = 0;724list->fds = NULL;725726/* Check if LISTEN_FDS is meant for this process. */727v = getenv("LISTEN_PID");728if (v == NULL)729return;730lpid = strtol(v, &end, 10);731if (end == NULL || end == v || *end != '\0' || lpid != getpid())732return;733734/* Get the number of activated sockets. */735v = getenv("LISTEN_FDS");736if (v == NULL)737return;738nfds = strtoul(v, &end, 10);739if (end == NULL || end == v || *end != '\0')740return;741if (nfds == 0 || nfds > (size_t)INT_MAX - SOCKACT_START)742return;743744list->fds = calloc(nfds, sizeof(*list->fds));745if (list->fds == NULL)746return;747748for (i = 0; i < nfds; i++) {749fd = i + SOCKACT_START;750set_cloexec_fd(fd);751slen = sizeof(list->fds[i].addr);752(void)getsockname(fd, ss2sa(&list->fds[i].addr), &slen);753slen = sizeof(list->fds[i].type);754(void)getsockopt(fd, SOL_SOCKET, SO_TYPE, &list->fds[i].type, &slen);755}756757list->nsockets = nfds;758}759760/* Release any storage used by *list. */761static void762fini_sockact_list(struct sockact_list *list)763{764free(list->fds);765list->fds = NULL;766list->nsockets = 0;767}768769/* If sa matches an address in *list, return the associated file descriptor and770* clear the address from *list. Otherwise return -1. */771static int772find_sockact(struct sockact_list *list, const struct sockaddr *sa, int type)773{774size_t i;775776for (i = 0; i < list->nsockets; i++) {777if (list->fds[i].type == type &&778sa_equal(ss2sa(&list->fds[i].addr), sa)) {779list->fds[i].type = -1;780memset(&list->fds[i].addr, 0, sizeof(list->fds[i].addr));781return i + SOCKACT_START;782}783}784return -1;785}786787/*788* Set up a listening socket.789*790* Arguments:791*792* - ba793* The bind address and port for the socket.794* - ai795* The addrinfo struct to use for creating the socket.796* - ctype797* The conn_type of this socket.798*/799static krb5_error_code800setup_socket(struct bind_address *ba, struct sockaddr *sock_address,801struct sockact_list *sockacts, void *handle, const char *prog,802verto_ctx *ctx, int listen_backlog, verto_callback vcb,803enum conn_type ctype)804{805krb5_error_code ret;806struct connection *conn;807verto_ev_flag flags;808verto_ev *ev = NULL;809int sock = -1;810char addrbuf[128];811812k5_print_addr_port(sock_address, addrbuf, sizeof(addrbuf));813krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"),814bind_type_names[ba->type], addrbuf);815816if (sockacts->nsockets > 0) {817/* Look for a systemd socket activation fd matching sock_address. */818sock = find_sockact(sockacts, sock_address, bind_socktypes[ba->type]);819if (sock == -1) {820/* Ignore configured addresses that don't match any caller-provided821* sockets. */822ret = 0;823goto cleanup;824}825} else {826/* We're not using socket activation; create the socket. */827ret = create_server_socket(sock_address, bind_socktypes[ba->type],828prog, &sock);829if (ret)830goto cleanup;831832/* Listen for backlogged connections on stream sockets. (For RPC833* sockets this will be done by svc_register().) */834if ((ba->type == TCP || ba->type == UNX) &&835listen(sock, listen_backlog) != 0) {836ret = errno;837com_err(prog, errno, _("Cannot listen on %s server socket on %s"),838bind_type_names[ba->type], addrbuf);839goto cleanup;840}841}842843/* Set non-blocking I/O for non-RPC listener sockets. */844if (ba->type != RPC && setnbio(sock) != 0) {845ret = errno;846com_err(prog, errno,847_("cannot set listening %s socket on %s non-blocking"),848bind_type_names[ba->type], addrbuf);849goto cleanup;850}851852/* Turn off the linger option for TCP sockets. */853if (ba->type == TCP && setnolinger(sock) != 0) {854ret = errno;855com_err(prog, errno, _("cannot set SO_LINGER on %s socket on %s"),856bind_type_names[ba->type], addrbuf);857goto cleanup;858}859860/* Try to turn on pktinfo for UDP wildcard sockets. */861if (ba->type == UDP && sa_is_wildcard(sock_address)) {862krb5_klog_syslog(LOG_DEBUG, _("Setting pktinfo on socket %s"),863addrbuf);864ret = set_pktinfo(sock, sock_address->sa_family);865if (ret) {866com_err(prog, ret,867_("Cannot request packet info for UDP socket address "868"%s port %d"), addrbuf, ba->port);869krb5_klog_syslog(LOG_INFO, _("System does not support pktinfo yet "870"binding to a wildcard address. "871"Packets are not guaranteed to "872"return on the received address."));873}874}875876/* Add the socket to the event loop. */877flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST |878VERTO_EV_FLAG_REINITIABLE;879ret = add_fd(sock, ctype, flags, handle, prog, ctx, vcb, &ev);880if (ret) {881krb5_klog_syslog(LOG_ERR, _("Error attempting to add verto event"));882goto cleanup;883}884885if (ba->type == RPC) {886conn = verto_get_private(ev);887conn->transp = svctcp_create(sock, 0, 0);888if (conn->transp == NULL) {889ret = errno;890krb5_klog_syslog(LOG_ERR, _("Cannot create RPC service: %s"),891strerror(ret));892goto cleanup;893}894895ret = svc_register(conn->transp, ba->rpc_svc_data.prognum,896ba->rpc_svc_data.versnum, ba->rpc_svc_data.dispatch,8970);898if (!ret) {899ret = errno;900krb5_klog_syslog(LOG_ERR, _("Cannot register RPC service: %s"),901strerror(ret));902goto cleanup;903}904}905906ev = NULL;907sock = -1;908ret = 0;909910cleanup:911if (sock >= 0)912close(sock);913if (ev != NULL)914verto_del(ev);915return ret;916}917918/*919* Setup all the socket addresses that the net-server should listen to.920*921* This function uses getaddrinfo to figure out all the addresses. This will922* automatically figure out which socket families that should be used on the923* host making it useful even for wildcard addresses.924*/925static krb5_error_code926setup_addresses(verto_ctx *ctx, void *handle, const char *prog,927int listen_backlog)928{929/* An bind_type enum map for the verto callback functions. */930static verto_callback *const verto_callbacks[] = {931[UDP] = &process_packet,932[TCP] = &accept_stream_connection,933[RPC] = &accept_rpc_connection,934[UNX] = &accept_stream_connection935};936krb5_error_code ret = 0;937size_t i;938int err, bound_any;939struct bind_address addr;940struct sockaddr_un sun;941struct addrinfo hints, *ai_list = NULL, *ai = NULL;942struct sockact_list sockacts = { 0 };943verto_callback vcb;944char addrbuf[128];945946/* Check to make sure addresses were added to the server. */947if (bind_addresses.n == 0) {948krb5_klog_syslog(LOG_ERR, _("No addresses added to the net server"));949return EINVAL;950}951952/* Ask for all address families, listener addresses, and no port name953* resolution. */954memset(&hints, 0, sizeof(struct addrinfo));955hints.ai_family = AF_UNSPEC;956hints.ai_flags = AI_PASSIVE;957#ifdef AI_NUMERICSERV958hints.ai_flags |= AI_NUMERICSERV;959#endif960961init_sockact_list(&sockacts);962963/* Add all the requested addresses. */964for (i = 0; i < bind_addresses.n; i++) {965addr = bind_addresses.data[i];966hints.ai_socktype = bind_socktypes[addr.type];967968if (addr.type == UNX) {969sun.sun_family = AF_UNIX;970if (strlcpy(sun.sun_path, addr.address, sizeof(sun.sun_path)) >=971sizeof(sun.sun_path)) {972ret = ENAMETOOLONG;973krb5_klog_syslog(LOG_ERR,974_("UNIX domain socket path too long: %s"),975addr.address);976goto cleanup;977}978ret = setup_socket(&addr, (struct sockaddr *)&sun, &sockacts,979handle, prog, ctx, listen_backlog,980verto_callbacks[addr.type],981bind_conn_types[addr.type]);982if (ret) {983krb5_klog_syslog(LOG_ERR,984_("Failed setting up a UNIX socket (for %s)"),985addr.address);986goto cleanup;987}988continue;989}990991/* Call getaddrinfo, using a dummy port value. */992err = getaddrinfo(addr.address, "0", &hints, &ai_list);993if (err) {994krb5_klog_syslog(LOG_ERR,995_("Failed getting address info (for %s): %s"),996(addr.address == NULL) ? "<wildcard>" :997addr.address, gai_strerror(err));998ret = EIO;999goto cleanup;1000}10011002/*1003* Loop through all the sockets that getaddrinfo could find to match1004* the requested address. For wildcard listeners, this should usually1005* have two results, one for each of IPv4 and IPv6, or one or the1006* other, depending on the system. On IPv4-only systems, getaddrinfo()1007* may return both IPv4 and IPv6 addresses, but creating an IPv6 socket1008* may give an EAFNOSUPPORT error, so tolerate that error as long as we1009* can bind at least one socket.1010*/1011bound_any = 0;1012for (ai = ai_list; ai != NULL; ai = ai->ai_next) {1013/* Make sure getaddrinfo returned a socket with the same type that1014* was requested. */1015assert(hints.ai_socktype == ai->ai_socktype);10161017/* Set the real port number. */1018sa_setport(ai->ai_addr, addr.port);10191020ret = setup_socket(&addr, ai->ai_addr, &sockacts, handle, prog,1021ctx, listen_backlog, verto_callbacks[addr.type],1022bind_conn_types[addr.type]);1023if (ret) {1024k5_print_addr(ai->ai_addr, addrbuf, sizeof(addrbuf));1025krb5_klog_syslog(LOG_ERR,1026_("Failed setting up a %s socket (for %s)"),1027bind_type_names[addr.type], addrbuf);1028if (ret != EAFNOSUPPORT)1029goto cleanup;1030} else {1031bound_any = 1;1032}1033}1034if (!bound_any)1035goto cleanup;1036ret = 0;10371038if (ai_list != NULL)1039freeaddrinfo(ai_list);1040ai_list = NULL;1041}10421043cleanup:1044if (ai_list != NULL)1045freeaddrinfo(ai_list);1046fini_sockact_list(&sockacts);1047return ret;1048}10491050krb5_error_code1051loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,1052int listen_backlog)1053{1054krb5_error_code ret;1055verto_ev *ev;1056int i;10571058/* Check to make sure that at least one address was added to the loop. */1059if (bind_addresses.n == 0)1060return EINVAL;10611062/* Close any open connections. */1063FOREACH_ELT(events, i, ev)1064verto_del(ev);1065events.n = 0;10661067krb5_klog_syslog(LOG_INFO, _("setting up network..."));1068ret = setup_addresses(ctx, handle, prog, listen_backlog);1069if (ret) {1070com_err(prog, ret, _("Error setting up network"));1071exit(1);1072}1073krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);1074if (events.n == 0) {1075/* If no sockets were set up, we can't continue. */1076com_err(prog, 0, _("no sockets set up?"));1077exit (1);1078}10791080return 0;1081}10821083struct udp_dispatch_state {1084void *handle;1085const char *prog;1086int port_fd;1087struct sockaddr_storage saddr;1088struct sockaddr_storage daddr;1089aux_addressing_info auxaddr;1090krb5_data request;1091char pktbuf[MAX_DGRAM_SIZE];1092};10931094static void1095process_packet_response(void *arg, krb5_error_code code, krb5_data *response)1096{1097struct udp_dispatch_state *state = arg;1098int cc;10991100if (code)1101com_err(state->prog ? state->prog : NULL, code,1102_("while dispatching (udp)"));1103if (code || response == NULL)1104goto out;11051106cc = send_to_from(state->port_fd, response->data,1107(socklen_t)response->length, 0, ss2sa(&state->saddr),1108ss2sa(&state->daddr), &state->auxaddr);1109if (cc == -1) {1110/* Note that the local address (daddr*) has no port number1111* info associated with it. */1112char sbuf[128], dbuf[128];1113int e = errno;11141115k5_print_addr_port(ss2sa(&state->saddr), sbuf, sizeof(sbuf));1116k5_print_addr(ss2sa(&state->daddr), dbuf, sizeof(dbuf));1117com_err(state->prog, e, _("while sending reply to %s from %s"),1118sbuf, dbuf);1119goto out;1120}1121if ((size_t)cc != response->length) {1122com_err(state->prog, 0, _("short reply write %d vs %d\n"),1123response->length, cc);1124}11251126out:1127krb5_free_data(get_context(state->handle), response);1128free(state);1129}11301131static void1132process_packet(verto_ctx *ctx, verto_ev *ev)1133{1134int cc;1135struct connection *conn;1136struct udp_dispatch_state *state;1137socklen_t slen;11381139conn = verto_get_private(ev);11401141state = malloc(sizeof(*state));1142if (!state) {1143com_err(conn->prog, ENOMEM, _("while dispatching (udp)"));1144return;1145}11461147state->handle = conn->handle;1148state->prog = conn->prog;1149state->port_fd = verto_get_fd(ev);1150assert(state->port_fd >= 0);11511152memset(&state->auxaddr, 0, sizeof(state->auxaddr));1153cc = recv_from_to(state->port_fd, state->pktbuf, sizeof(state->pktbuf), 0,1154&state->saddr, &state->daddr, &state->auxaddr);1155if (cc == -1) {1156if (errno != EINTR && errno != EAGAIN1157/*1158* This is how Linux indicates that a previous transmission was1159* refused, e.g., if the client timed out before getting the1160* response packet.1161*/1162&& errno != ECONNREFUSED1163)1164com_err(conn->prog, errno, _("while receiving from network"));1165free(state);1166return;1167}1168if (!cc) { /* zero-length packet? */1169free(state);1170return;1171}11721173if (state->daddr.ss_family == AF_UNSPEC && conn->type == CONN_UDP) {1174/*1175* An address couldn't be obtained, so the PKTINFO option probably1176* isn't available. If the socket is bound to a specific address, then1177* try to get the address here.1178*/1179slen = sizeof(state->daddr);1180(void)getsockname(state->port_fd, ss2sa(&state->daddr), &slen);1181}11821183state->request.length = cc;1184state->request.data = state->pktbuf;11851186dispatch(state->handle, ss2sa(&state->daddr), ss2sa(&state->saddr),1187&state->request, 0, ctx, process_packet_response, state);1188}11891190static int1191kill_lru_stream_connection(void *handle, verto_ev *newev)1192{1193struct connection *c = NULL, *oldest_c = NULL;1194verto_ev *ev, *oldest_ev = NULL;1195int i, fd = -1;11961197krb5_klog_syslog(LOG_INFO, _("too many connections"));11981199FOREACH_ELT (events, i, ev) {1200if (ev == newev)1201continue;12021203c = verto_get_private(ev);1204if (!c)1205continue;1206if (c->type != CONN_TCP && c->type != CONN_RPC &&1207c->type != CONN_UNIXSOCK)1208continue;1209if (oldest_c == NULL1210|| oldest_c->start_time > c->start_time) {1211oldest_ev = ev;1212oldest_c = c;1213}1214}1215if (oldest_c != NULL) {1216krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),1217conn_type_names[oldest_c->type],1218verto_get_fd(oldest_ev), oldest_c->addrbuf);1219if (oldest_c->type == CONN_RPC)1220oldest_c->rpc_force_close = 1;1221verto_del(oldest_ev);1222}1223return fd;1224}12251226static void1227accept_stream_connection(verto_ctx *ctx, verto_ev *ev)1228{1229int s;1230struct sockaddr_storage addr;1231socklen_t addrlen = sizeof(addr);1232struct connection *newconn, *conn;1233enum conn_type ctype;1234verto_ev_flag flags;1235verto_ev *newev;12361237conn = verto_get_private(ev);1238s = accept(verto_get_fd(ev), ss2sa(&addr), &addrlen);1239if (s < 0)1240return;1241set_cloexec_fd(s);1242#ifndef _WIN321243if (s >= FD_SETSIZE) {1244close(s);1245return;1246}1247#endif1248setnbio(s);1249setnolinger(s);1250if (addr.ss_family != AF_UNIX)1251setkeepalive(s);12521253flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;1254ctype = (conn->type == CONN_TCP_LISTENER) ? CONN_TCP : CONN_UNIXSOCK;1255if (add_fd(s, ctype, flags, conn->handle, conn->prog, ctx,1256process_stream_connection_read, &newev) != 0) {1257close(s);1258return;1259}1260newconn = verto_get_private(newev);12611262if (addr.ss_family == AF_UNIX) {1263/* accept() doesn't fill in sun_path as the client socket isn't bound.1264* For logging purposes we will use the target address. */1265addrlen = sizeof(addr);1266if (getsockname(s, ss2sa(&addr), &addrlen) < 0) {1267com_err(conn->prog, errno, _("Failed to get address for %d"), s);1268close(s);1269return;1270}1271}12721273k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,1274sizeof(newconn->addrbuf));1275newconn->addr_s = addr;1276newconn->addrlen = addrlen;1277newconn->bufsiz = 1024 * 1024;1278newconn->buffer = malloc(newconn->bufsiz);1279newconn->start_time = time(0);12801281if (++stream_data_counter > max_stream_data_connections)1282kill_lru_stream_connection(conn->handle, newev);12831284if (newconn->buffer == 0) {1285com_err(conn->prog, errno,1286_("allocating buffer for new TCP session from %s"),1287newconn->addrbuf);1288verto_del(newev);1289return;1290}1291newconn->offset = 0;1292SG_SET(&newconn->sgbuf[0], newconn->lenbuf, 4);1293SG_SET(&newconn->sgbuf[1], 0, 0);1294}12951296struct tcp_dispatch_state {1297struct sockaddr_storage local_saddr;1298struct connection *conn;1299krb5_data request;1300verto_ctx *ctx;1301int sock;1302};13031304static void1305process_stream_response(void *arg, krb5_error_code code, krb5_data *response)1306{1307struct tcp_dispatch_state *state = arg;1308verto_ev *ev;13091310assert(state);1311state->conn->response = response;13121313if (code)1314com_err(state->conn->prog, code, _("while dispatching (tcp)"));1315if (code || !response)1316goto kill_tcp_connection;13171318/* Queue outgoing response. */1319store_32_be(response->length, state->conn->lenbuf);1320SG_SET(&state->conn->sgbuf[1], response->data, response->length);1321state->conn->sgp = state->conn->sgbuf;1322state->conn->sgnum = 2;13231324ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,1325process_stream_connection_write, state->sock, state->conn);1326if (ev) {1327free(state);1328return;1329}13301331kill_tcp_connection:1332stream_data_counter--;1333free_connection(state->conn);1334close(state->sock);1335free(state);1336}13371338/* Creates the tcp_dispatch_state and deletes the verto event. */1339static struct tcp_dispatch_state *1340prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)1341{1342struct tcp_dispatch_state *state;13431344state = malloc(sizeof(*state));1345if (!state) {1346krb5_klog_syslog(LOG_ERR, _("error allocating tcp dispatch private!"));1347return NULL;1348}1349state->conn = verto_get_private(ev);1350state->sock = verto_get_fd(ev);1351state->ctx = ctx;1352verto_set_private(ev, NULL, NULL); /* Don't close the fd or free conn! */1353remove_event_from_set(ev); /* Remove it from the set. */1354verto_del(ev);1355return state;1356}13571358static void1359process_stream_connection_read(verto_ctx *ctx, verto_ev *ev)1360{1361struct tcp_dispatch_state *state = NULL;1362struct connection *conn = NULL;1363ssize_t nread;1364size_t len;13651366conn = verto_get_private(ev);13671368/*1369* Read message length and data into one big buffer, already allocated1370* at connect time. If we have a complete message, we stop reading, so1371* we should only be here if there is no data in the buffer, or only an1372* incomplete message.1373*/1374if (conn->offset < 4) {1375krb5_data *response = NULL;13761377/* msglen has not been computed. XXX Doing at least two reads1378* here, letting the kernel worry about buffering. */1379len = 4 - conn->offset;1380nread = SOCKET_READ(verto_get_fd(ev),1381conn->buffer + conn->offset, len);1382if (nread < 0) /* error */1383goto kill_tcp_connection;1384if (nread == 0) /* eof */1385goto kill_tcp_connection;1386conn->offset += nread;1387if (conn->offset == 4) {1388unsigned char *p = (unsigned char *)conn->buffer;1389conn->msglen = load_32_be(p);1390if (conn->msglen > conn->bufsiz - 4) {1391krb5_error_code err;1392/* Message too big. */1393krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, "1394"cap is %lu"), conn->addrbuf,1395(unsigned long) conn->msglen,1396(unsigned long) conn->bufsiz - 4);1397/* XXX Should return an error. */1398err = make_toolong_error (conn->handle,1399&response);1400if (err) {1401krb5_klog_syslog(LOG_ERR, _("error constructing "1402"KRB_ERR_FIELD_TOOLONG error! %s"),1403error_message(err));1404goto kill_tcp_connection;1405}14061407state = prepare_for_dispatch(ctx, ev);1408if (!state) {1409krb5_free_data(get_context(conn->handle), response);1410goto kill_tcp_connection;1411}1412process_stream_response(state, 0, response);1413}1414}1415} else {1416/* msglen known. */1417socklen_t local_saddrlen = sizeof(struct sockaddr_storage);14181419len = conn->msglen - (conn->offset - 4);1420nread = SOCKET_READ(verto_get_fd(ev),1421conn->buffer + conn->offset, len);1422if (nread < 0) /* error */1423goto kill_tcp_connection;1424if (nread == 0) /* eof */1425goto kill_tcp_connection;1426conn->offset += nread;1427if (conn->offset < conn->msglen + 4)1428return;14291430/* Have a complete message, and exactly one message. */1431state = prepare_for_dispatch(ctx, ev);1432if (!state)1433goto kill_tcp_connection;14341435state->request.length = conn->msglen;1436state->request.data = conn->buffer + 4;14371438if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr),1439&local_saddrlen) < 0) {1440krb5_klog_syslog(LOG_ERR, _("getsockname failed: %s"),1441error_message(errno));1442goto kill_tcp_connection;1443}1444dispatch(state->conn->handle, ss2sa(&state->local_saddr),1445ss2sa(&conn->addr_s), &state->request, 1, ctx,1446process_stream_response, state);1447}14481449return;14501451kill_tcp_connection:1452verto_del(ev);1453}14541455static void1456process_stream_connection_write(verto_ctx *ctx, verto_ev *ev)1457{1458struct connection *conn;1459SOCKET_WRITEV_TEMP tmp;1460ssize_t nwrote;1461int sock;14621463conn = verto_get_private(ev);1464sock = verto_get_fd(ev);14651466nwrote = SOCKET_WRITEV(sock, conn->sgp,1467conn->sgnum, tmp);1468if (nwrote > 0) { /* non-error and non-eof */1469while (nwrote) {1470sg_buf *sgp = conn->sgp;1471if ((size_t)nwrote < SG_LEN(sgp)) {1472SG_ADVANCE(sgp, (size_t)nwrote);1473nwrote = 0;1474} else {1475nwrote -= SG_LEN(sgp);1476conn->sgp++;1477conn->sgnum--;1478if (conn->sgnum == 0 && nwrote != 0)1479abort();1480}1481}14821483/* If we still have more data to send, just return so that1484* the main loop can call this function again when the socket1485* is ready for more writing. */1486if (conn->sgnum > 0)1487return;1488}14891490/* Finished sending. We should go back to reading, though if we1491* sent a FIELD_TOOLONG error in reply to a length with the high1492* bit set, RFC 4120 says we have to close the TCP stream. */1493verto_del(ev);1494}14951496void1497loop_free(verto_ctx *ctx)1498{1499int i;1500struct bind_address val;15011502verto_free(ctx);15031504/* Free each addresses added to the loop. */1505FOREACH_ELT(bind_addresses, i, val)1506free(val.address);1507FREE_SET_DATA(bind_addresses);1508FREE_SET_DATA(events);1509}15101511static int1512have_event_for_fd(int fd)1513{1514verto_ev *ev;1515int i;15161517FOREACH_ELT(events, i, ev) {1518if (verto_get_fd(ev) == fd)1519return 1;1520}15211522return 0;1523}15241525static void1526accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)1527{1528verto_ev_flag flags;1529struct connection *conn;1530fd_set fds;1531int s;15321533conn = verto_get_private(ev);15341535/* Service the woken RPC listener descriptor. */1536FD_ZERO(&fds);1537FD_SET(verto_get_fd(ev), &fds);1538svc_getreqset(&fds);15391540/* Scan svc_fdset for any new connections. */1541for (s = 0; s < FD_SETSIZE; s++) {1542struct sockaddr_storage addr;1543socklen_t addrlen = sizeof(addr);1544struct connection *newconn;1545verto_ev *newev;15461547/* If we already have this fd, continue. */1548if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s))1549continue;15501551flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;1552if (add_fd(s, CONN_RPC, flags, conn->handle, conn->prog, ctx,1553process_rpc_connection, &newev) != 0)1554continue;1555newconn = verto_get_private(newev);15561557set_cloexec_fd(s);15581559if (getpeername(s, ss2sa(&addr), &addrlen) != 0) {1560strlcpy(newconn->addrbuf, "<unknown>", sizeof(newconn->addrbuf));1561} else {1562k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,1563sizeof(newconn->addrbuf));1564}15651566newconn->addr_s = addr;1567newconn->addrlen = addrlen;1568newconn->start_time = time(0);15691570if (++stream_data_counter > max_stream_data_connections)1571kill_lru_stream_connection(newconn->handle, newev);1572}1573}15741575static void1576process_rpc_connection(verto_ctx *ctx, verto_ev *ev)1577{1578fd_set fds;15791580FD_ZERO(&fds);1581FD_SET(verto_get_fd(ev), &fds);1582svc_getreqset(&fds);15831584if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))1585verto_del(ev);1586}15871588#endif /* INET */158915901591