diff options
author | assar <assar@FreeBSD.org> | 2000-12-29 21:00:22 +0000 |
---|---|---|
committer | assar <assar@FreeBSD.org> | 2000-12-29 21:00:22 +0000 |
commit | 2aa51584a1bbbfd8d631a114c91b525674ec0175 (patch) | |
tree | 3be1655d8572aa7a94f884419466a6be1d5e9e35 /crypto/kerberosIV/kadmin/admin_server.c | |
parent | 7e5f2377be4220b42ea18ddd0112a4a64320943a (diff) | |
download | FreeBSD-src-2aa51584a1bbbfd8d631a114c91b525674ec0175.zip FreeBSD-src-2aa51584a1bbbfd8d631a114c91b525674ec0175.tar.gz |
import krb4-1.0.5
Diffstat (limited to 'crypto/kerberosIV/kadmin/admin_server.c')
-rw-r--r-- | crypto/kerberosIV/kadmin/admin_server.c | 356 |
1 files changed, 253 insertions, 103 deletions
diff --git a/crypto/kerberosIV/kadmin/admin_server.c b/crypto/kerberosIV/kadmin/admin_server.c index c1f2a8e..14347fd 100644 --- a/crypto/kerberosIV/kadmin/admin_server.c +++ b/crypto/kerberosIV/kadmin/admin_server.c @@ -30,7 +30,7 @@ or implied warranty. #include "kadm_locl.h" -RCSID("$Id: admin_server.c,v 1.49 1999/11/13 06:32:19 assar Exp $"); +RCSID("$Id: admin_server.c,v 1.49.2.2 2000/10/18 20:24:57 assar Exp $"); /* Almost all procs and such need this, so it is global */ admin_params prm; /* The command line parameters struct */ @@ -39,8 +39,16 @@ admin_params prm; /* The command line parameters struct */ char *acldir = DEFAULT_ACL_DIR; static char krbrlm[REALM_SZ]; -static unsigned pidarraysize = 0; -static int *pidarray = NULL; +#define MAXCHILDREN 100 + +struct child { + pid_t pid; + int pipe_fd; + int authenticated; +}; + +static unsigned nchildren = 0; +static struct child children[MAXCHILDREN]; static int exit_now = 0; @@ -52,46 +60,26 @@ doexit(int sig) SIGRETURN(0); } +static sig_atomic_t do_wait; + static RETSIGTYPE do_child(int sig) { - int pid; - int i, j; - - int status; - - pid = wait(&status); - - /* Reinstall signal handlers for SysV. Must be done *after* wait */ - signal(SIGCHLD, do_child); - - for (i = 0; i < pidarraysize; i++) - if (pidarray[i] == pid) { - /* found it */ - for (j = i; j < pidarraysize-1; j++) - /* copy others down */ - pidarray[j] = pidarray[j+1]; - pidarraysize--; - if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) - || WIFSIGNALED(status)) - krb_log("child %d: termsig %d, retcode %d", pid, - WTERMSIG(status), WEXITSTATUS(status)); - SIGRETURN(0); - } - krb_log("child %d not in list: termsig %d, retcode %d", pid, - WTERMSIG(status), WEXITSTATUS(status)); + do_wait = 1; SIGRETURN(0); } + static void kill_children(void) { int i; - for (i = 0; i < pidarraysize; i++) { - kill(pidarray[i], SIGINT); - krb_log("killing child %d", pidarray[i]); + for (i = 0; i < nchildren; i++) { + kill(children[i].pid, SIGINT); + close (children[i].pipe_fd); + krb_log("killing child %d", children[i].pid); } } @@ -117,11 +105,6 @@ clear_secrets(void) server_parm.master_key_version = 0L; } -#ifdef DEBUG -#define cleanexit(code) {kerb_fini(); return;} -#endif - -#ifndef DEBUG static void cleanexit(int val) { @@ -129,10 +112,21 @@ cleanexit(int val) clear_secrets(); exit(val); } -#endif + +static RETSIGTYPE +sigalrm(int sig) +{ + cleanexit(1); +} + +/* + * handle the client on the socket `fd' from `who' + * `signal_fd' is a pipe on which to signal when the user has been + * authenticated + */ static void -process_client(int fd, struct sockaddr_in *who) +process_client(int fd, struct sockaddr_in *who, int signal_fd) { u_char *dat; int dat_len; @@ -142,6 +136,13 @@ process_client(int fd, struct sockaddr_in *who) des_cblock skey; int more; int status; + int authenticated = 0; + + /* make this connection time-out after 1 second if the user has + not managed one transaction succesfully in kadm_ser_in */ + + signal(SIGALRM, sigalrm); + alarm(2); #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) { @@ -230,8 +231,19 @@ process_client(int fd, struct sockaddr_in *who) if (exit_now) { cleanexit(0); } - if ((retval = kadm_ser_in(&dat, &dat_len, errpkt)) != KADM_SUCCESS) + retval = kadm_ser_in(&dat, &dat_len, errpkt); + + if (retval == KADM_SUCCESS) { + if (!authenticated) { + unsigned char one = 1; + + authenticated = 1; + alarm (0); + write (signal_fd, &one, 1); + } + } else { krb_log("processing request: %s", error_message(retval)); + } /* kadm_ser_in did the processing and returned stuff in dat & dat_len , return the appropriate data */ @@ -255,6 +267,175 @@ process_client(int fd, struct sockaddr_in *who) /*NOTREACHED*/ } +static void +accept_client (int admin_fd) +{ + int pipe_fd[2]; + int addrlen; + struct sockaddr_in peer; + pid_t pid; + int peer_fd; + + /* using up the maximum number of children, try to get rid + of one unauthenticated one */ + + if (nchildren >= MAXCHILDREN) { + int i, nunauth = 0; + int victim; + + for (;;) { + for (i = 0; i < nchildren; ++i) + if (children[i].authenticated == 0) + ++nunauth; + if (nunauth == 0) + return; + + victim = rand() % nchildren; + if (children[victim].authenticated == 0) { + kill(children[victim].pid, SIGINT); + close(children[victim].pipe_fd); + for (i = victim; i < nchildren; ++i) + children[i] = children[i + 1]; + --nchildren; + break; + } + } + } + + /* accept the conn */ + addrlen = sizeof(peer); + peer_fd = accept(admin_fd, (struct sockaddr *)&peer, &addrlen); + if (peer_fd < 0) { + krb_log("accept: %s",error_message(errno)); + return; + } + if (pipe (pipe_fd) < 0) { + krb_log ("pipe: %s", error_message(errno)); + return; + } + + if (pipe_fd[0] >= FD_SETSIZE + || pipe_fd[1] >= FD_SETSIZE) { + krb_log ("pipe fds too large"); + close (pipe_fd[0]); + close (pipe_fd[1]); + return; + } + + pid = fork (); + + if (pid < 0) { + krb_log ("fork: %s", error_message(errno)); + close (pipe_fd[0]); + close (pipe_fd[1]); + return; + } + + if (pid != 0) { + /* parent */ + /* fork succeded: keep tabs on child */ + close(peer_fd); + children[nchildren].pid = pid; + children[nchildren].pipe_fd = pipe_fd[0]; + children[nchildren].authenticated = 0; + ++nchildren; + close (pipe_fd[1]); + + } else { + int i; + + /* child */ + close(admin_fd); + close(pipe_fd[0]); + + for (i = 0; i < nchildren; ++i) + close (children[i].pipe_fd); + + /* + * If we are multihomed we need to figure out which + * local address that is used this time since it is + * used in "direction" comparison. + */ + getsockname(peer_fd, + (struct sockaddr *)&server_parm.admin_addr, + &addrlen); + /* do stuff */ + process_client (peer_fd, &peer, pipe_fd[1]); + } +} + +/* + * handle data signaled from child `child' kadmind + */ + +static void +handle_child_signal (int child) +{ + int ret; + unsigned char data[1]; + + ret = read (children[child].pipe_fd, data, 1); + if (ret < 0) { + if (errno != EINTR) + krb_log ("read from child %d: %s", child, + error_message(errno)); + return; + } + if (ret == 0) { + close (children[child].pipe_fd); + children[child].pipe_fd = -1; + return; + } + if (data) + children[child].authenticated = 1; +} + +/* + * handle dead children + */ + +static void +handle_sigchld (void) +{ + pid_t pid; + int status; + int i, j; + + for (;;) { + int found = 0; + + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); + if (pid == 0 || (pid < 0 && errno == ECHILD)) + break; + if (pid < 0) { + krb_log("waitpid: %s", error_message(errno)); + break; + } + for (i = 0; i < nchildren; i++) + if (children[i].pid == pid) { + /* found it */ + close(children[i].pipe_fd); + for (j = i; j < nchildren; j++) + /* copy others down */ + children[j] = children[j+1]; + --nchildren; +#if 0 + if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) + || WIFSIGNALED(status)) + krb_log("child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); +#endif + found = 1; + } +#if 0 + if (!found) + krb_log("child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); +#endif + } + do_wait = 0; +} + /* kadm_listen listen on the admin servers port for a request @@ -264,11 +445,7 @@ kadm_listen(void) { int found; int admin_fd; - int peer_fd; - fd_set mask, readfds; - struct sockaddr_in peer; - int addrlen; - int pid; + fd_set readfds; signal(SIGINT, doexit); signal(SIGTERM, doexit); @@ -282,9 +459,15 @@ kadm_listen(void) if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return KADM_NO_SOCK; + + if (admin_fd >= FD_SETSIZE) { + krb_log("admin_fd too big"); + return KADM_NO_BIND; + } + #if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT) { - int one=1; + int one = 1; setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); } @@ -292,76 +475,43 @@ kadm_listen(void) if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, sizeof(struct sockaddr_in)) < 0) return KADM_NO_BIND; - listen(admin_fd, 1); - FD_ZERO(&mask); - FD_SET(admin_fd, &mask); + if (listen(admin_fd, SOMAXCONN) < 0) + return KADM_NO_BIND; for (;;) { /* loop nearly forever */ + int i; + int maxfd = -1; + if (exit_now) { clear_secrets(); kill_children(); return(0); } - readfds = mask; - if ((found = select(admin_fd+1, &readfds, 0, - 0, (struct timeval *)0)) == 0) - continue; /* no things read */ + if (do_wait) + handle_sigchld (); + + FD_ZERO(&readfds); + FD_SET(admin_fd, &readfds); + maxfd = max(maxfd, admin_fd); + for (i = 0; i < nchildren; ++i) + if (children[i].pipe_fd >= 0) { + FD_SET(children[i].pipe_fd, &readfds); + maxfd = max(maxfd, children[i].pipe_fd); + } + + found = select(maxfd + 1, &readfds, NULL, NULL, NULL); if (found < 0) { if (errno != EINTR) krb_log("select: %s",error_message(errno)); continue; - } - if (FD_ISSET(admin_fd, &readfds)) { - /* accept the conn */ - addrlen = sizeof(peer); - if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, - &addrlen)) < 0) { - krb_log("accept: %s",error_message(errno)); - continue; - } -#ifndef DEBUG - /* if you want a sep daemon for each server */ - if ((pid = fork())) { - void *tmp; - - /* parent */ - if (pid < 0) { - krb_log("fork: %s",error_message(errno)); - close(peer_fd); - continue; - } - /* fork succeded: keep tabs on child */ - close(peer_fd); - tmp = realloc(pidarray, - (pidarraysize + 1) * sizeof(*pidarray)); - if(tmp == NULL) { - krb_log ("malloc: no memory. pid %u on its own", - (unsigned)pid); - } else { - pidarray = tmp; - pidarray[pidarraysize++] = pid; - } - } else { - /* child */ - close(admin_fd); -#endif /* DEBUG */ - /* - * If we are multihomed we need to figure out which - * local address that is used this time since it is - * used in "direction" comparison. - */ - getsockname(peer_fd, - (struct sockaddr *)&server_parm.admin_addr, - &addrlen); - /* do stuff */ - process_client (peer_fd, &peer); -#ifndef DEBUG - } -#endif - } else { - krb_log("something else woke me up!"); - return(0); } + if (FD_ISSET(admin_fd, &readfds)) + accept_client (admin_fd); + for (i = 0; i < nchildren; ++i) + if (children[i].pipe_fd >= 0 + && FD_ISSET(children[i].pipe_fd, &readfds)) { + handle_child_signal (i); + } } /*NOTREACHED*/ } |