172445Sassar/*
2233294Sstas * Copyright (c) 2000 - 2004 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
572445Sassar *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
972445Sassar *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1272445Sassar *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1672445Sassar *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2072445Sassar *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3272445Sassar */
3372445Sassar
3472445Sassar#include "kadmin_locl.h"
3572445Sassar#ifdef HAVE_SYS_WAIT_H
3672445Sassar#include <sys/wait.h>
3772445Sassar#endif
3872445Sassar
3972445Sassarstruct kadm_port {
4072445Sassar    char *port;
4172445Sassar    unsigned short def_port;
4272445Sassar    struct kadm_port *next;
4372445Sassar} *kadm_ports;
4472445Sassar
4572445Sassarstatic void
46233294Sstasadd_kadm_port(krb5_context contextp, const char *service, unsigned int port)
4772445Sassar{
4872445Sassar    struct kadm_port *p;
4972445Sassar    p = malloc(sizeof(*p));
5072445Sassar    if(p == NULL) {
51233294Sstas	krb5_warnx(contextp, "failed to allocate %lu bytes\n",
5272445Sassar		   (unsigned long)sizeof(*p));
5372445Sassar	return;
5472445Sassar    }
55233294Sstas
5672445Sassar    p->port = strdup(service);
5772445Sassar    p->def_port = port;
5872445Sassar
5972445Sassar    p->next = kadm_ports;
6072445Sassar    kadm_ports = p;
6172445Sassar}
6272445Sassar
6372445Sassarstatic void
64233294Sstasadd_standard_ports (krb5_context contextp)
6572445Sassar{
66233294Sstas    add_kadm_port(contextp, "kerberos-adm", 749);
6772445Sassar}
6872445Sassar
6972445Sassar/*
7072445Sassar * parse the set of space-delimited ports in `str' and add them.
7172445Sassar * "+" => all the standard ones
7272445Sassar * otherwise it's port|service[/protocol]
7372445Sassar */
7472445Sassar
7572445Sassarvoid
76233294Sstasparse_ports(krb5_context contextp, const char *str)
7772445Sassar{
7872445Sassar    char p[128];
7972445Sassar
8072445Sassar    while(strsep_copy(&str, " \t", p, sizeof(p)) != -1) {
8172445Sassar	if(strcmp(p, "+") == 0)
82233294Sstas	    add_standard_ports(contextp);
8372445Sassar	else
84233294Sstas	    add_kadm_port(contextp, p, 0);
8572445Sassar    }
8672445Sassar}
8772445Sassar
8872445Sassarstatic pid_t pgrp;
8972445Sassarsig_atomic_t term_flag, doing_useful_work;
9072445Sassar
9172445Sassarstatic RETSIGTYPE
9272445Sassarsigchld(int sig)
9372445Sassar{
9472445Sassar    int status;
95233294Sstas    /*
96233294Sstas     * waitpid() is async safe. will return -1 or 0 on no more zombie
97233294Sstas     * children
98233294Sstas     */
99233294Sstas    while ((waitpid(-1, &status, WNOHANG)) > 0)
100233294Sstas	;
10172445Sassar    SIGRETURN(0);
10272445Sassar}
10372445Sassar
10472445Sassarstatic RETSIGTYPE
10572445Sassarterminate(int sig)
10672445Sassar{
10772445Sassar    if(getpid() == pgrp) {
10872445Sassar	/* parent */
10972445Sassar	term_flag = 1;
11072445Sassar	signal(sig, SIG_IGN);
11172445Sassar	killpg(pgrp, sig);
11272445Sassar    } else {
11372445Sassar	/* child */
11472445Sassar	if(doing_useful_work)
11572445Sassar	    term_flag = 1;
11672445Sassar	else
11772445Sassar	    exit(0);
11872445Sassar    }
11972445Sassar    SIGRETURN(0);
12072445Sassar}
12172445Sassar
12272445Sassarstatic int
123233294Sstasspawn_child(krb5_context contextp, int *socks,
124233294Sstas	    unsigned int num_socks, int this_sock)
12572445Sassar{
126233294Sstas    int e;
127233294Sstas    size_t i;
12872445Sassar    struct sockaddr_storage __ss;
12972445Sassar    struct sockaddr *sa = (struct sockaddr *)&__ss;
13072445Sassar    socklen_t sa_size = sizeof(__ss);
131233294Sstas    krb5_socket_t s;
13272445Sassar    pid_t pid;
13372445Sassar    krb5_address addr;
13472445Sassar    char buf[128];
13572445Sassar    size_t buf_len;
13672445Sassar
13772445Sassar    s = accept(socks[this_sock], sa, &sa_size);
138233294Sstas    if(rk_IS_BAD_SOCKET(s)) {
139233294Sstas	krb5_warn(contextp, rk_SOCK_ERRNO, "accept");
14072445Sassar	return 1;
14172445Sassar    }
142233294Sstas    e = krb5_sockaddr2address(contextp, sa, &addr);
14372445Sassar    if(e)
144233294Sstas	krb5_warn(contextp, e, "krb5_sockaddr2address");
14572445Sassar    else {
146233294Sstas	e = krb5_print_address (&addr, buf, sizeof(buf),
14772445Sassar				&buf_len);
148233294Sstas	if(e)
149233294Sstas	    krb5_warn(contextp, e, "krb5_print_address");
15072445Sassar	else
151233294Sstas	    krb5_warnx(contextp, "connection from %s", buf);
152233294Sstas	krb5_free_address(contextp, &addr);
15372445Sassar    }
154233294Sstas
15572445Sassar    pid = fork();
15672445Sassar    if(pid == 0) {
15772445Sassar	for(i = 0; i < num_socks; i++)
158233294Sstas	    rk_closesocket(socks[i]);
15972445Sassar	dup2(s, STDIN_FILENO);
16072445Sassar	dup2(s, STDOUT_FILENO);
16172445Sassar	if(s != STDIN_FILENO && s != STDOUT_FILENO)
162233294Sstas	    rk_closesocket(s);
16372445Sassar	return 0;
16472445Sassar    } else {
165233294Sstas	rk_closesocket(s);
16672445Sassar    }
16772445Sassar    return 1;
16872445Sassar}
16972445Sassar
170233294Sstasstatic void
171233294Sstaswait_for_connection(krb5_context contextp,
172233294Sstas		    krb5_socket_t *socks, unsigned int num_socks)
17372445Sassar{
174233294Sstas    unsigned int i;
175233294Sstas    int e;
17672445Sassar    fd_set orig_read_set, read_set;
177233294Sstas    int status, max_fd = -1;
178233294Sstas
17972445Sassar    FD_ZERO(&orig_read_set);
180233294Sstas
18172445Sassar    for(i = 0; i < num_socks; i++) {
182233294Sstas#ifdef FD_SETSIZE
18372445Sassar	if (socks[i] >= FD_SETSIZE)
18472445Sassar	    errx (1, "fd too large");
185233294Sstas#endif
18672445Sassar	FD_SET(socks[i], &orig_read_set);
18772445Sassar	max_fd = max(max_fd, socks[i]);
18872445Sassar    }
189233294Sstas
19072445Sassar    pgrp = getpid();
19172445Sassar
19272445Sassar    if(setpgid(0, pgrp) < 0)
19372445Sassar	err(1, "setpgid");
19472445Sassar
19572445Sassar    signal(SIGTERM, terminate);
19672445Sassar    signal(SIGINT, terminate);
19772445Sassar    signal(SIGCHLD, sigchld);
19872445Sassar
19972445Sassar    while (term_flag == 0) {
20072445Sassar	read_set = orig_read_set;
20172445Sassar	e = select(max_fd + 1, &read_set, NULL, NULL, NULL);
202233294Sstas	if(rk_IS_SOCKET_ERROR(e)) {
203233294Sstas	    if(rk_SOCK_ERRNO != EINTR)
204233294Sstas		krb5_warn(contextp, rk_SOCK_ERRNO, "select");
20572445Sassar	} else if(e == 0)
206233294Sstas	    krb5_warnx(contextp, "select returned 0");
20772445Sassar	else {
20872445Sassar	    for(i = 0; i < num_socks; i++) {
20972445Sassar		if(FD_ISSET(socks[i], &read_set))
210233294Sstas		    if(spawn_child(contextp, socks, num_socks, i) == 0)
211233294Sstas			return;
21272445Sassar	    }
21372445Sassar	}
21472445Sassar    }
21572445Sassar    signal(SIGCHLD, SIG_IGN);
216233294Sstas
217233294Sstas    while ((waitpid(-1, &status, WNOHANG)) > 0)
218233294Sstas	;
219233294Sstas
22072445Sassar    exit(0);
22172445Sassar}
22272445Sassar
22372445Sassar
224233294Sstasvoid
225233294Sstasstart_server(krb5_context contextp, const char *port_str)
22672445Sassar{
22772445Sassar    int e;
22872445Sassar    struct kadm_port *p;
22972445Sassar
230233294Sstas    krb5_socket_t *socks = NULL, *tmp;
231233294Sstas    unsigned int num_socks = 0;
23272445Sassar    int i;
23372445Sassar
234233294Sstas    if (port_str == NULL)
235233294Sstas	port_str = "+";
236233294Sstas
237233294Sstas    parse_ports(contextp, port_str);
238233294Sstas
23972445Sassar    for(p = kadm_ports; p; p = p->next) {
24072445Sassar	struct addrinfo hints, *ai, *ap;
24172445Sassar	char portstr[32];
24272445Sassar	memset (&hints, 0, sizeof(hints));
24372445Sassar	hints.ai_flags    = AI_PASSIVE;
24472445Sassar	hints.ai_socktype = SOCK_STREAM;
24572445Sassar
24672445Sassar	e = getaddrinfo(NULL, p->port, &hints, &ai);
24772445Sassar	if(e) {
24872445Sassar	    snprintf(portstr, sizeof(portstr), "%u", p->def_port);
24972445Sassar	    e = getaddrinfo(NULL, portstr, &hints, &ai);
25072445Sassar	}
25172445Sassar
25272445Sassar	if(e) {
253233294Sstas	    krb5_warn(contextp, krb5_eai_to_heim_errno(e, errno),
25478527Sassar		      "%s", portstr);
25572445Sassar	    continue;
25672445Sassar	}
25772445Sassar	i = 0;
258233294Sstas	for(ap = ai; ap; ap = ap->ai_next)
25972445Sassar	    i++;
26072445Sassar	tmp = realloc(socks, (num_socks + i) * sizeof(*socks));
26172445Sassar	if(tmp == NULL) {
262233294Sstas	    krb5_warnx(contextp, "failed to reallocate %lu bytes",
26372445Sassar		       (unsigned long)(num_socks + i) * sizeof(*socks));
26472445Sassar	    continue;
26572445Sassar	}
26672445Sassar	socks = tmp;
26772445Sassar	for(ap = ai; ap; ap = ap->ai_next) {
268233294Sstas	    krb5_socket_t s = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
269233294Sstas	    if(rk_IS_BAD_SOCKET(s)) {
270233294Sstas		krb5_warn(contextp, rk_SOCK_ERRNO, "socket");
27172445Sassar		continue;
27272445Sassar	    }
273178825Sdfr
274178825Sdfr	    socket_set_reuseaddr(s, 1);
275178825Sdfr	    socket_set_ipv6only(s, 1);
276178825Sdfr
277233294Sstas	    if (rk_IS_SOCKET_ERROR(bind (s, ap->ai_addr, ap->ai_addrlen))) {
278233294Sstas		krb5_warn(contextp, rk_SOCK_ERRNO, "bind");
279233294Sstas		rk_closesocket(s);
28072445Sassar		continue;
28172445Sassar	    }
282233294Sstas	    if (rk_IS_SOCKET_ERROR(listen (s, SOMAXCONN))) {
283233294Sstas		krb5_warn(contextp, rk_SOCK_ERRNO, "listen");
284233294Sstas		rk_closesocket(s);
28572445Sassar		continue;
28672445Sassar	    }
28772445Sassar	    socks[num_socks++] = s;
28872445Sassar	}
28972445Sassar	freeaddrinfo (ai);
29072445Sassar    }
29172445Sassar    if(num_socks == 0)
292233294Sstas	krb5_errx(contextp, 1, "no sockets to listen to - exiting");
293233294Sstas
294233294Sstas    wait_for_connection(contextp, socks, num_socks);
29572445Sassar}
296