2 Copyright (C) 1989 by the Massachusetts Institute of Technology
4 Export of this software from the United States of America is assumed
5 to require a specific license from the United States Government.
6 It is the responsibility of any person or organization contemplating
7 export to obtain such a license before exporting.
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission. M.I.T. makes no representations about the suitability of
17 this software for any purpose. It is provided "as is" without express
23 * Top-level loop of the kerberos Administration server
28 this holds the main loop and initialization and cleanup code for the server
31 #include "kadm_locl.h"
33 RCSID("$Id: admin_server.c,v 1.49.2.2 2000/10/18 20:24:57 assar Exp $");
35 /* Almost all procs and such need this, so it is global */
36 admin_params prm; /* The command line parameters struct */
39 char *acldir = DEFAULT_ACL_DIR;
40 static char krbrlm[REALM_SZ];
42 #define MAXCHILDREN 100
50 static unsigned nchildren = 0;
51 static struct child children[MAXCHILDREN];
53 static int exit_now = 0;
63 static sig_atomic_t do_wait;
79 for (i = 0; i < nchildren; i++) {
80 kill(children[i].pid, SIGINT);
81 close (children[i].pipe_fd);
82 krb_log("killing child %d", children[i].pid);
86 /* close the system log file */
90 krb_log("Shutting down admin server");
94 byebye(void) /* say goodnight gracie */
96 printf("Admin Server (kadm server) has completed operation.\n");
102 memset(server_parm.master_key, 0, sizeof(server_parm.master_key));
103 memset(server_parm.master_key_schedule, 0,
104 sizeof(server_parm.master_key_schedule));
105 server_parm.master_key_version = 0L;
123 * handle the client on the socket `fd' from `who'
124 * `signal_fd' is a pipe on which to signal when the user has been
129 process_client(int fd, struct sockaddr_in *who, int signal_fd)
139 int authenticated = 0;
141 /* make this connection time-out after 1 second if the user has
142 not managed one transaction succesfully in kadm_ser_in */
144 signal(SIGALRM, sigalrm);
147 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
151 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
152 (void *)&on, sizeof(on)) < 0)
153 krb_log("setsockopt keepalive: %d",errno);
157 server_parm.recv_addr = *who;
159 if (kerb_init()) { /* Open as client */
160 krb_log("can't open krb db");
163 /* need to set service key to changepw.KRB_MASTER */
165 status = kerb_get_principal(server_parm.sname, server_parm.sinst, &service,
171 dat_len = KADM_VERSIZE + 4;
172 dat = (u_char *) malloc(dat_len);
174 krb_log("malloc failed");
178 memcpy(pdat, KADM_ULOSE, KADM_VERSIZE);
179 krb_put_int (KADM_DB_INUSE, pdat + KADM_VERSIZE, 4, 4);
181 } else if (!status) {
182 krb_log("no service %s.%s",server_parm.sname, server_parm.sinst);
186 copy_to_key(&service.key_low, &service.key_high, skey);
187 memset(&service, 0, sizeof(service));
188 kdb_encrypt_key (&skey, &skey, &server_parm.master_key,
189 server_parm.master_key_schedule, DES_DECRYPT);
190 krb_set_key(skey, 0); /* if error, will show up when
192 memset(skey, 0, sizeof(skey));
197 errpkt = malloc(KADM_VERSIZE + 4);
198 if (errpkt == NULL) {
199 krb_log("malloc: no memory");
204 if ((retval = krb_net_read(fd, &dlen, sizeof(u_short))) !=
207 krb_log("dlen read: %s",error_message(errno));
209 krb_log("short dlen read: %d",retval);
211 cleanexit(retval ? 3 : 0);
216 dat_len = ntohs(dlen);
217 dat = (u_char *) malloc(dat_len);
219 krb_log("malloc: No memory");
223 if ((retval = krb_net_read(fd, dat, dat_len)) != dat_len) {
225 krb_log("data read: %s",error_message(errno));
227 krb_log("short read: %d vs. %d", dat_len, retval);
234 retval = kadm_ser_in(&dat, &dat_len, errpkt);
236 if (retval == KADM_SUCCESS) {
237 if (!authenticated) {
238 unsigned char one = 1;
242 write (signal_fd, &one, 1);
245 krb_log("processing request: %s", error_message(retval));
248 /* kadm_ser_in did the processing and returned stuff in
249 dat & dat_len , return the appropriate data */
252 dlen = htons(dat_len);
254 if (krb_net_write(fd, &dlen, sizeof(u_short)) < 0) {
255 krb_log("writing dlen to client: %s",error_message(errno));
260 if (krb_net_write(fd, dat, dat_len) < 0) {
261 krb_log("writing to client: %s", error_message(errno));
271 accept_client (int admin_fd)
275 struct sockaddr_in peer;
279 /* using up the maximum number of children, try to get rid
280 of one unauthenticated one */
282 if (nchildren >= MAXCHILDREN) {
287 for (i = 0; i < nchildren; ++i)
288 if (children[i].authenticated == 0)
293 victim = rand() % nchildren;
294 if (children[victim].authenticated == 0) {
295 kill(children[victim].pid, SIGINT);
296 close(children[victim].pipe_fd);
297 for (i = victim; i < nchildren; ++i)
298 children[i] = children[i + 1];
305 /* accept the conn */
306 addrlen = sizeof(peer);
307 peer_fd = accept(admin_fd, (struct sockaddr *)&peer, &addrlen);
309 krb_log("accept: %s",error_message(errno));
312 if (pipe (pipe_fd) < 0) {
313 krb_log ("pipe: %s", error_message(errno));
317 if (pipe_fd[0] >= FD_SETSIZE
318 || pipe_fd[1] >= FD_SETSIZE) {
319 krb_log ("pipe fds too large");
328 krb_log ("fork: %s", error_message(errno));
336 /* fork succeded: keep tabs on child */
338 children[nchildren].pid = pid;
339 children[nchildren].pipe_fd = pipe_fd[0];
340 children[nchildren].authenticated = 0;
351 for (i = 0; i < nchildren; ++i)
352 close (children[i].pipe_fd);
355 * If we are multihomed we need to figure out which
356 * local address that is used this time since it is
357 * used in "direction" comparison.
360 (struct sockaddr *)&server_parm.admin_addr,
363 process_client (peer_fd, &peer, pipe_fd[1]);
368 * handle data signaled from child `child' kadmind
372 handle_child_signal (int child)
375 unsigned char data[1];
377 ret = read (children[child].pipe_fd, data, 1);
380 krb_log ("read from child %d: %s", child,
381 error_message(errno));
385 close (children[child].pipe_fd);
386 children[child].pipe_fd = -1;
390 children[child].authenticated = 1;
394 * handle dead children
398 handle_sigchld (void)
407 pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
408 if (pid == 0 || (pid < 0 && errno == ECHILD))
411 krb_log("waitpid: %s", error_message(errno));
414 for (i = 0; i < nchildren; i++)
415 if (children[i].pid == pid) {
417 close(children[i].pipe_fd);
418 for (j = i; j < nchildren; j++)
419 /* copy others down */
420 children[j] = children[j+1];
423 if ((WIFEXITED(status) && WEXITSTATUS(status) != 0)
424 || WIFSIGNALED(status))
425 krb_log("child %d: termsig %d, retcode %d", pid,
426 WTERMSIG(status), WEXITSTATUS(status));
432 krb_log("child %d not in list: termsig %d, retcode %d", pid,
433 WTERMSIG(status), WEXITSTATUS(status));
441 listen on the admin servers port for a request
450 signal(SIGINT, doexit);
451 signal(SIGTERM, doexit);
452 signal(SIGHUP, doexit);
453 signal(SIGQUIT, doexit);
454 signal(SIGPIPE, SIG_IGN); /* get errors on write() */
455 signal(SIGALRM, doexit);
456 signal(SIGCHLD, do_child);
458 krb_log("setsid() failed");
460 if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
463 if (admin_fd >= FD_SETSIZE) {
464 krb_log("admin_fd too big");
468 #if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
471 setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one,
475 if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr,
476 sizeof(struct sockaddr_in)) < 0)
478 if (listen(admin_fd, SOMAXCONN) < 0)
481 for (;;) { /* loop nearly forever */
494 FD_SET(admin_fd, &readfds);
495 maxfd = max(maxfd, admin_fd);
496 for (i = 0; i < nchildren; ++i)
497 if (children[i].pipe_fd >= 0) {
498 FD_SET(children[i].pipe_fd, &readfds);
499 maxfd = max(maxfd, children[i].pipe_fd);
502 found = select(maxfd + 1, &readfds, NULL, NULL, NULL);
505 krb_log("select: %s",error_message(errno));
508 if (FD_ISSET(admin_fd, &readfds))
509 accept_client (admin_fd);
510 for (i = 0; i < nchildren; ++i)
511 if (children[i].pipe_fd >= 0
512 && FD_ISSET(children[i].pipe_fd, &readfds)) {
513 handle_child_signal (i);
520 ** Main does the logical thing, it sets up the database and RPC interface,
521 ** as well as handling the creation and maintenance of the syslog file...
524 main(int argc, char **argv) /* admin_server main routine */
528 struct in_addr i_addr;
530 set_progname (argv[0]);
532 umask(077); /* Create protected files */
534 i_addr.s_addr = INADDR_ANY;
535 /* initialize the admin_params structure */
536 prm.sysfile = KADM_SYSLOG; /* default file name */
539 memset(krbrlm, 0, sizeof(krbrlm));
541 while ((c = getopt(argc, argv, "f:hmnd:a:r:i:")) != -1)
543 case 'f': /* Syslog file name change */
544 prm.sysfile = optarg;
552 case 'a': /* new acl directory */
556 /* put code to deal with alt database place */
557 if ((errval = kerb_db_set_name(optarg)))
558 errx (1, "opening database %s: %s",
559 optarg, error_message(errval));
562 strlcpy (krbrlm, optarg, sizeof(krbrlm));
565 /* Only listen on this address */
566 if(inet_aton (optarg, &i_addr) == 0) {
567 fprintf (stderr, "Bad address: %s\n", optarg);
571 case 'h': /* get help on using admin_server */
573 errx(1, "Usage: kadmind [-h] [-n] [-m] [-r realm] [-d dbname] [-f filename] [-a acldir] [-i address_to_listen_on]");
577 if (krb_get_lrealm(krbrlm, 1) != KSUCCESS)
578 errx (1, "Unable to get local realm. Fix krb.conf or use -r.");
580 printf("KADM Server %s initializing\n",KADM_VERSTR);
581 printf("Please do not use 'kill -9' to kill this job, use a\n");
582 printf("regular kill instead\n\n");
584 kset_logfile(prm.sysfile);
585 krb_log("Admin server starting");
587 kerb_db_set_lockmode(KERB_DBL_NONBLOCKING);
588 errval = kerb_init(); /* Open the Kerberos database */
590 warnx ("error: kerb_init() failed");
594 /* set up the server_parm struct */
595 if ((errval = kadm_ser_init(prm.inter, krbrlm, i_addr))==KADM_SUCCESS) {
596 kerb_fini(); /* Close the Kerberos database--
597 will re-open later */
598 errval = kadm_listen(); /* listen for calls to server from
601 if (errval != KADM_SUCCESS) {
602 warnx("error: %s",error_message(errval));
603 kerb_fini(); /* Close if error */
605 close_syslog(); /* Close syslog file, print
607 byebye(); /* Say bye bye on the terminal
610 } /* procedure main */