Cleanup debug messages.
[shishi.git] / src / shishid.c
blob01a4d4b2859a35ceb524b7e075149d401d5e4bd0
1 /* shishid.c Shishi Key Distribution Center daemon.
2 * Copyright (C) 2002, 2003 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #if HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #ifdef STDC_HEADERS
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <ctype.h>
31 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
41 #if defined HAVE_DECL_H_ERRNO && !HAVE_DECL_H_ERRNO
42 /* extern int h_errno; */
43 #endif
45 #ifdef HAVE_PWD_H
46 #include <pwd.h>
47 #endif
49 #ifdef HAVE_SYS_TYPES_H
50 #include <sys/types.h>
51 #endif
53 #ifdef HAVE_SYS_SELECT_H
54 #include <sys/select.h>
55 #endif
57 #ifdef HAVE_SYS_SOCKET_H
58 #include <sys/socket.h>
59 #endif
61 #ifdef HAVE_SYS_IOCTL_H
62 #include <sys/ioctl.h>
63 #endif
65 #ifdef HAVE_ERRNO_H
66 #include <errno.h>
67 #endif
69 #if HAVE_INTTYPES_H
70 # include <inttypes.h>
71 #else
72 # if HAVE_STDINT_H
73 # include <stdint.h>
74 # endif
75 #endif
77 #if TIME_WITH_SYS_TIME
78 # include <sys/time.h>
79 # include <time.h>
80 #else
81 # if HAVE_SYS_TIME_H
82 # include <sys/time.h>
83 # else
84 # include <time.h>
85 # endif
86 #endif
88 #if HAVE_STRING_H
89 # if !STDC_HEADERS && HAVE_MEMORY_H
90 # include <memory.h>
91 # endif
92 # include <string.h>
93 #endif
94 #if HAVE_STRINGS_H
95 # include <strings.h>
96 #endif
98 #ifdef HAVE_SIGNAL_H
99 #include <signal.h>
100 #endif
102 #ifdef HAVE_NETINET_IN_H
103 #include <netinet/in.h>
104 #endif
105 #ifdef HAVE_NETINET_IN6_H
106 #include <netinet/in6.h>
107 #endif
109 #ifdef HAVE_ARPA_INET_H
110 #include <arpa/inet.h>
111 #endif
113 #ifdef HAVE_SYSLOG_H
114 #include <syslog.h>
115 #endif
117 #include <shishi.h>
118 #include <argp.h>
120 #define FAMILY_IPV4 "IPv4"
121 #define FAMILY_IPV6 "IPv6"
123 #ifdef WITH_IPV6
124 #define LISTEN_DEFAULT FAMILY_IPV4 ":*:kerberos/udp, " \
125 FAMILY_IPV4 ":*:kerberos/tcp, " \
126 FAMILY_IPV6 ":*:kerberos/udp, " \
127 FAMILY_IPV6 ":*:kerberos/tcp"
128 #else
129 #define LISTEN_DEFAULT "*:kerberos/udp, *:kerberos/tcp"
130 #endif
132 const char *program_name = PACKAGE;
134 struct listenspec
136 char *str;
137 int family;
138 struct sockaddr addr;
139 int port;
140 int type;
141 int sockfd;
142 char buf[BUFSIZ];
143 size_t bufpos;
146 struct arguments
148 int silent, verbose;
149 char *cfgfile;
150 char *keyfile;
151 char *setuid;
152 struct listenspec *listenspec;
153 int nlistenspec;
154 Shishi_key *tgskey;
157 const char *argp_program_version = PACKAGE_STRING;
158 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
160 static error_t
161 parse_opt (int key, char *arg, struct argp_state *state)
163 struct arguments *arguments = state->input;
164 char *ptrptr;
165 char *val;
166 int i;
168 switch (key)
170 case 'q':
171 case 's':
172 arguments->silent = 1;
173 break;
175 case 'v':
176 arguments->verbose = 1;
177 break;
179 case 'c':
180 arguments->cfgfile = strdup (arg);
181 break;
183 case 'k':
184 arguments->keyfile = strdup (arg);
185 break;
187 case 'u':
188 arguments->setuid = strdup (arg);
189 break;
191 case ARGP_KEY_END:
192 if (arguments->nlistenspec > 0)
193 break;
194 arg = strdup (LISTEN_DEFAULT);
195 /* fall through */
197 case 'l':
198 for (i = 0; (val = strtok_r (i == 0 ? arg : NULL, ", \t", &ptrptr));
199 i++)
201 char *service, *proto;
202 struct servent *se;
203 struct hostent *he;
204 struct listenspec *ls;
205 struct sockaddr_in *sin;
206 #ifdef WITH_IPV6
207 struct sockaddr_in6 *sin6;
208 #endif
210 arguments->nlistenspec++;
211 arguments->listenspec = realloc (arguments->listenspec,
212 sizeof (*arguments->listenspec) *
213 arguments->nlistenspec);
214 if (arguments->listenspec == NULL)
215 argp_error (state, "Fatal memory allocation error");
216 ls = &arguments->listenspec[arguments->nlistenspec - 1];
217 ls->str = strdup (val);
218 ls->bufpos = 0;
219 sin = (struct sockaddr_in *) &ls->addr;
220 #ifdef WITH_IPV6
221 sin6 = (struct sockaddr_in6 *) &ls->addr;
222 #endif
224 proto = strrchr (val, '/');
225 if (proto == NULL)
226 argp_error (state, "Could not find type in listen spec: `%s'",
227 ls->str);
228 *proto = '\0';
229 proto++;
231 if (strcmp (proto, "tcp") == 0)
232 ls->type = SOCK_STREAM;
233 else
234 ls->type = SOCK_DGRAM;
236 service = strrchr (val, ':');
237 if (service == NULL)
238 argp_error (state, "Could not find service in listen spec: `%s'",
239 ls->str);
240 *service = '\0';
241 service++;
243 se = getservbyname (service, proto);
244 if (se)
245 ls->port = ntohs (se->s_port);
246 else if (strcmp (service, "kerberos") == 0)
247 ls->port = 88;
248 else if (atoi (service) != 0)
249 ls->port = atoi (service);
250 else
251 argp_error (state, "Unknown service `%s' in listen spec: `%s'",
252 service, ls->str);
254 #ifdef WITH_IPV6
255 if (ls->family == AF_INET6)
256 sin6->sin6_port = htons (ls->port);
257 else
258 #endif
259 sin->sin_port = htons (ls->port);
261 if (strncmp (val, FAMILY_IPV4 ":", strlen (FAMILY_IPV4 ":")) == 0)
263 ls->family = AF_INET;
264 val += strlen (FAMILY_IPV4 ":");
266 #ifdef WITH_IPV6
267 else if (strncmp (val, FAMILY_IPV6 ":", strlen (FAMILY_IPV6 ":")) ==
270 ls->family = AF_INET6;
271 val += strlen (FAMILY_IPV6 ":");
273 #endif
274 else
275 ls->family = AF_INET;
277 if (strcmp (val, "*") == 0)
279 #ifdef WITH_IPV6
280 if (ls->family == AF_INET6)
281 sin6->sin6_addr = in6addr_any;
282 else
283 #endif
284 sin->sin_addr.s_addr = htonl (INADDR_ANY);
286 else if ((he = gethostbyname (val)))
288 if (he->h_addrtype == AF_INET)
290 sin->sin_family = AF_INET;
291 memcpy (&sin->sin_addr, he->h_addr_list[0], he->h_length);
293 #ifdef WITH_IPV6
294 else if (he->h_addrtype == AF_INET6)
296 sin6->sin6_family = AF_INET6;
297 memcpy (&sin6->sin6_addr, he->h_addr_list[0], he->h_length);
299 #endif
300 else
301 argp_error (state, "Unknown protocol family (%d) returned "
302 "by gethostbyname(\"%s\"): `%s'", he->h_addrtype,
303 val, ls->str);
305 else
306 argp_error (state, "Unknown host `%s' in listen spec: `%s'",
307 val, ls->str);
310 break;
312 case ARGP_KEY_ARG:
313 argp_error (state, "Too many arguments: `%s'", arg);
314 break;
316 default:
317 return ARGP_ERR_UNKNOWN;
320 return 0;
323 static struct argp_option options[] = {
325 {"verbose", 'v', 0, 0,
326 "Produce verbose output.", 0},
328 {"quiet", 'q', 0, 0,
329 "Don't produce any output.", 0},
331 {"silent", 's', 0, OPTION_ALIAS,
332 NULL, 0},
334 {"configuration-file", 'c', "FILE", 0,
335 "Read configuration from file. Default is " SYSTEMCFGFILE ".", 0},
337 {"listen", 'l', "[FAMILY:]ADDRESS:SERVICE/TYPE,...", 0,
338 "What to listen on. Family is \"IPv4\" or \"IPv6\", if absent the "
339 "family is decided by gethostbyname(ADDRESS). An address of \"*\" "
340 "indicates all addresses on the local host. "
341 "The default is \"" LISTEN_DEFAULT "\".", 0},
343 {"key-file", 'k', "FILE", 0,
344 "Read keys from file. Default is " KDCKEYFILE ".", 0},
346 {"setuid", 'u', "NAME", 0,
347 "After binding socket, set user identity.", 0},
349 {NULL, 0, NULL, 0,
350 NULL, 0}
353 static struct argp argp = {
354 options,
355 parse_opt,
356 NULL,
357 "Shishid -- Key Distribution Center network daemon",
358 NULL,
359 NULL,
360 NULL
363 static char *fatal_krberror;
364 static size_t fatal_krberror_len;
366 static int
367 setup_fatal_krberror (Shishi * handle)
369 Shishi_asn1 krberr;
370 int rc;
372 krberr = shishi_krberror (handle);
373 if (!krberr)
374 return SHISHI_MALLOC_ERROR;
376 rc = shishi_krberror_set_etext (handle, krberr,
377 "Internal KDC error, contact administrator");
378 if (rc != SHISHI_OK)
379 return rc;
381 rc = shishi_krberror_der (handle, krberr, &fatal_krberror,
382 &fatal_krberror_len);
383 if (rc != SHISHI_OK)
384 return rc;
386 return SHISHI_OK;
389 static int
390 asreq1 (Shishi * handle, struct arguments *arg, Shishi_as * as)
392 Shishi_tkt *tkt;
393 Shishi_key *sessionkey, *userkey;
394 int etype, i;
395 char buf[BUFSIZ];
396 size_t buflen;
397 int err;
398 char *username, *servername, *realm;
400 tkt = shishi_as_tkt (as);
401 if (!tkt)
402 return SHISHI_MALLOC_ERROR;
404 i = 1;
407 err = shishi_kdcreq_etype (handle, shishi_as_req (as), &etype, i);
408 if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
409 break;
411 while (err == SHISHI_OK);
412 if (err != SHISHI_OK)
413 return err;
415 /* XXX use a "preferred server kdc etype" from shishi instead? */
417 err = shishi_key_random (handle, etype, &sessionkey);
418 if (err)
419 return err;
421 err = shishi_tkt_key_set (tkt, sessionkey);
422 if (err)
423 return err;
425 buflen = sizeof (buf) - 1;
426 err = shishi_kdcreq_cname_get (handle, shishi_as_req (as), buf, &buflen);
427 if (err != SHISHI_OK)
428 return err;
429 buf[buflen] = '\0';
430 username = strdup (buf);
431 printf ("username %s\n", username);
433 buflen = sizeof (buf) - 1;
434 err = shishi_kdcreq_sname_get (handle, shishi_as_req (as), buf, &buflen);
435 if (err != SHISHI_OK)
436 return err;
437 buf[buflen] = '\0';
438 servername = strdup (buf);
439 printf ("servername %s\n", servername);
441 buflen = sizeof (buf) - 1;
442 err = shishi_kdcreq_realm_get (handle, shishi_as_req (as), buf, &buflen);
443 if (err != SHISHI_OK)
444 return err;
445 buf[buflen] = '\0';
446 realm = strdup (buf);
447 printf ("client & server realm %s\n", realm);
449 err = shishi_tkt_clientrealm_set (tkt, realm, username);
450 if (err)
451 return err;
453 err = shishi_tkt_serverrealm_set (tkt, realm, servername);
454 if (err)
455 return err;
457 userkey = shishi_keys_for_serverrealm_in_file (handle,
458 arg->keyfile,
459 username, realm);
460 if (!userkey)
461 return !SHISHI_OK;
463 err = shishi_tkt_build (tkt, arg->tgskey);
464 if (err)
465 return err;
467 err = shishi_as_rep_build (as, userkey);
468 if (err)
469 return err;
471 if (arg->verbose)
473 shishi_kdcreq_print (handle, stderr, shishi_as_req (as));
474 shishi_encticketpart_print (handle, stderr,
475 shishi_tkt_encticketpart (tkt));
476 shishi_ticket_print (handle, stderr, shishi_tkt_ticket (tkt));
477 shishi_enckdcreppart_print (handle, stderr,
478 shishi_tkt_enckdcreppart (tkt));
479 shishi_kdcrep_print (handle, stderr, shishi_as_rep (as));
482 return SHISHI_OK;
485 static void
486 asreq (Shishi * handle, struct arguments *arg,
487 Shishi_asn1 kdcreq, char **out, size_t *outlen)
489 Shishi_as *as;
490 int rc;
492 rc = shishi_as (handle, &as);
493 if (rc != SHISHI_OK)
495 syslog (LOG_ERR, "Incoming request failed: Cannot create AS: %s\n",
496 shishi_strerror (rc));
497 /* XXX hard coded KRB-ERROR? */
498 *out = strdup ("foo");
499 *outlen = strlen (*out);
500 return;
503 shishi_as_req_set (as, kdcreq);
505 rc = asreq1 (handle, arg, as);
506 if (rc != SHISHI_OK)
508 syslog (LOG_NOTICE, "Could not answer request: %s: %s\n",
509 shishi_strerror (rc),
510 shishi_krberror_message (handle, shishi_as_krberror (as)));
511 rc = shishi_as_krberror_der (as, out, outlen);
513 else
514 rc = shishi_as_rep_der (as, out, outlen);
515 if (rc != SHISHI_OK)
517 syslog (LOG_ERR,
518 "Incoming request failed: Cannot DER encode reply: %s\n",
519 shishi_strerror (rc));
520 /* XXX hard coded KRB-ERROR? */
521 *out = strdup ("aaaaaa");
522 *outlen = strlen (*out);
523 return;
526 return;
528 static int
529 tgsreq1 (Shishi * handle, struct arguments *arg, Shishi_tgs * tgs)
531 int rc;
532 Shishi_tkt *tkt;
533 Shishi_key *sessionkey, *sessiontktkey, *serverkey, *subkey, *keytouse;
534 char buf[BUFSIZ];
535 int buflen;
536 int err;
537 char *username, *servername, *realm;
538 int32_t etype;
539 int i;
541 tkt = shishi_tgs_tkt (tgs);
542 if (!tkt)
543 return SHISHI_MALLOC_ERROR;
545 i = 1;
548 err = shishi_kdcreq_etype (handle, shishi_tgs_req (tgs), &etype, i);
549 if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
550 break;
552 while (err == SHISHI_OK);
553 if (err != SHISHI_OK)
554 return err;
556 /* XXX use a "preferred server kdc etype" from shishi instead? */
558 err = shishi_key_random (handle, etype, &sessionkey);
559 if (err)
560 return err;
562 err = shishi_tkt_key_set (tkt, sessionkey);
563 if (err)
564 return err;
566 /* extract pa-data and populate tgs->ap */
567 rc = shishi_tgs_req_process (tgs);
568 if (rc != SHISHI_OK)
569 return rc;
571 /* XXX check if ticket is for our tgt key */
573 /* decrypt ticket with our key, and decrypt authenticator using key in tkt */
574 rc = shishi_ap_req_process_keyusage
575 (shishi_tgs_ap (tgs), arg->tgskey,
576 SHISHI_KEYUSAGE_TGSREQ_APREQ_AUTHENTICATOR);
577 if (rc != SHISHI_OK)
578 return rc;
580 /* XXX check that checksum in authenticator match tgsreq.req-body */
582 buflen = sizeof (buf) - 1;
583 err = shishi_encticketpart_cname_get
584 (handle, shishi_tkt_encticketpart (shishi_ap_tkt (shishi_tgs_ap (tgs))),
585 buf, &buflen);
586 if (err != SHISHI_OK)
587 return err;
588 buf[buflen] = '\0';
589 username = strdup (buf);
590 printf ("username %s\n", username);
592 buflen = sizeof (buf) - 1;
593 err = shishi_kdcreq_sname_get (handle, shishi_tgs_req (tgs), buf, &buflen);
594 if (err != SHISHI_OK)
595 return err;
596 buf[buflen] = '\0';
597 servername = strdup (buf);
598 printf ("servername %s\n", servername);
600 buflen = sizeof (buf) - 1;
601 err = shishi_kdcreq_realm_get (handle, shishi_tgs_req (tgs), buf, &buflen);
602 if (err != SHISHI_OK)
603 return err;
604 buf[buflen] = '\0';
605 realm = strdup (buf);
606 printf ("server realm %s\n", realm);
608 err = shishi_tkt_clientrealm_set (tkt, realm, username);
609 if (err)
610 return err;
612 err = shishi_tkt_serverrealm_set (tkt, realm, servername);
613 if (err)
614 return err;
616 serverkey = shishi_keys_for_serverrealm_in_file (handle,
617 arg->keyfile,
618 servername, realm);
619 if (!serverkey)
620 return !SHISHI_OK;
622 err = shishi_tkt_build (tkt, serverkey);
623 if (err)
624 return err;
626 err = shishi_encticketpart_get_key
627 (handle,
628 shishi_tkt_encticketpart (shishi_ap_tkt (shishi_tgs_ap (tgs))),
629 &sessiontktkey);
631 err = shishi_authenticator_get_subkey
632 (handle, shishi_ap_authenticator (shishi_tgs_ap (tgs)), &subkey);
633 if (err != SHISHI_OK && err != SHISHI_ASN1_NO_ELEMENT)
634 return err;
636 if (err == SHISHI_OK)
637 keytouse = subkey;
638 else
639 keytouse = sessiontktkey;
641 err = shishi_tgs_rep_build (tgs, keytouse);
642 if (err)
643 return err;
645 if (arg->verbose)
647 puts ("KDC-REQ in:");
648 shishi_kdcreq_print (handle, stderr, shishi_tgs_req (tgs));
649 puts ("AP-REQ in KDC-REQ:");
650 shishi_apreq_print (handle, stderr,
651 shishi_ap_req (shishi_tgs_ap (tgs)));
652 puts ("Authenticator in AP-REQ in KDC-REQ:");
653 shishi_authenticator_print (handle, stderr, shishi_ap_authenticator
654 (shishi_tgs_ap (tgs)));
655 puts ("Ticket in AP-REQ:");
656 shishi_ticket_print (handle, stdout,
657 shishi_tkt_ticket
658 (shishi_ap_tkt (shishi_tgs_ap (tgs))));
659 puts ("EncTicketPart in AP-REQ:");
660 shishi_encticketpart_print (handle, stdout,
661 shishi_tkt_encticketpart
662 (shishi_ap_tkt (shishi_tgs_ap (tgs))));
663 puts ("Ticket in TGS-REP:");
664 shishi_ticket_print (handle, stdout, shishi_tkt_ticket (tkt));
665 puts ("EncTicketPart in TGS-REP:");
666 shishi_encticketpart_print (handle, stderr,
667 shishi_tkt_encticketpart (tkt));
668 puts ("EncKDCRepPart in TGS-REP:");
669 shishi_enckdcreppart_print (handle, stderr,
670 shishi_tkt_enckdcreppart (tkt));
671 puts ("KDC-REP:");
672 shishi_kdcrep_print (handle, stderr, shishi_tgs_rep (tgs));
675 return SHISHI_OK;
678 static void
679 tgsreq (Shishi * handle, struct arguments *arg,
680 Shishi_asn1 kdcreq, char **out, size_t *outlen)
682 Shishi_tgs *tgs;
683 int rc;
685 rc = shishi_tgs (handle, &tgs);
686 if (rc != SHISHI_OK)
688 syslog (LOG_ERR, "Incoming request failed: Cannot create TGS: %s\n",
689 shishi_strerror (rc));
690 /* XXX hard coded KRB-ERROR? */
691 *out = strdup ("foo");
692 *outlen = strlen (*out);
693 return;
696 shishi_tgs_req_set (tgs, kdcreq);
698 rc = tgsreq1 (handle, arg, tgs);
699 if (rc != SHISHI_OK)
701 syslog (LOG_NOTICE, "Could not answer request: %s: %s\n",
702 shishi_strerror (rc),
703 shishi_krberror_message (handle, shishi_tgs_krberror (tgs)));
704 rc = shishi_tgs_krberror_der (tgs, out, outlen);
706 else
707 rc = shishi_tgs_rep_der (tgs, out, outlen);
708 if (rc != SHISHI_OK)
710 syslog (LOG_ERR,
711 "Incoming request failed: Cannot DER encode reply: %s\n",
712 shishi_strerror (rc));
713 /* XXX hard coded KRB-ERROR? */
714 *out = strdup ("aaaaaa");
715 *outlen = strlen (*out);
716 return;
719 return;
722 static Shishi_msgtype
723 get_msgtype (Shishi * handle, char *in, size_t inlen)
725 if (inlen > 1 && *in >= 0x60 && (unsigned char) *in <= 0x7F)
726 return *in - 0x60;
727 else
728 return 0;
731 static int
732 process_1 (Shishi * handle, struct arguments *arg,
733 char *in, size_t inlen, char **out, size_t * outlen)
735 Shishi_asn1 kdcreq;
736 Shishi_msgtype msgtype;
737 Shishi_asn1 krberr;
738 int rc;
740 krberr = shishi_krberror (handle);
741 if (!krberr)
742 return SHISHI_MALLOC_ERROR;
744 fprintf (stderr, "Processing %d bytes...\n", inlen);
746 msgtype = get_msgtype (handle, in, inlen);
748 fprintf (stderr, "ASN.1 msg-type %d (0x%x)...\n", msgtype, msgtype);
750 switch (msgtype)
752 case SHISHI_MSGTYPE_AS_REQ:
753 kdcreq = shishi_der2asn1_asreq (handle, in, inlen);
754 if (kdcreq)
756 asreq (handle, arg, kdcreq, out, outlen);
757 return SHISHI_OK;
759 else
761 rc = shishi_krberror_set_etext (handle, krberr,
762 "Cannot parse AS-REQ");
763 if (rc != SHISHI_OK)
764 return rc;
766 break;
768 case SHISHI_MSGTYPE_TGS_REQ:
769 kdcreq = shishi_der2asn1_tgsreq (handle, in, inlen);
770 if (kdcreq)
772 tgsreq (handle, arg, kdcreq, out, outlen);
773 return SHISHI_OK;
775 else
777 rc = shishi_krberror_set_etext (handle, krberr,
778 "Cannot parse TGS-REQ");
779 if (rc != SHISHI_OK)
780 return rc;
782 break;
784 default:
785 rc = shishi_krberror_set_etext (handle, krberr,
786 "Unsupported message type");
787 if (rc != SHISHI_OK)
788 return rc;
789 break;
792 rc = shishi_krberror_der (handle, krberr, out, outlen);
793 if (rc != SHISHI_OK)
794 return rc;
796 return SHISHI_OK;
799 static void
800 process (Shishi * handle, struct arguments *arg,
801 char *in, int inlen, char **out, size_t * outlen)
803 int rc;
805 *out = NULL;
806 *outlen = 0;
808 rc = process_1 (handle, arg, in, inlen, out, outlen);
810 if (rc != SHISHI_OK || *out == NULL || *outlen == 0)
812 *out = fatal_krberror;
813 *outlen = fatal_krberror_len;
817 int quit = 0;
819 static void
820 ctrlc (int signum)
822 quit = 1;
825 static int
826 kdc_listen (struct arguments *arg)
828 struct listenspec *ls;
829 int maxfd = 0;
830 int i;
831 int yes;
833 for (i = 0; i < arg->nlistenspec; i++)
835 ls = &arg->listenspec[i];
837 if (!arg->silent)
838 printf ("Listening on %s...", ls->str);
840 ls->sockfd = socket (ls->family, ls->type, 0);
841 if (ls->sockfd < 0)
843 if (!arg->silent)
844 printf ("failed\n");
845 perror ("socket");
846 ls->sockfd = 0;
847 continue;
850 yes = 1;
851 if (setsockopt (ls->sockfd, SOL_SOCKET, SO_REUSEADDR,
852 (char *) &yes, sizeof (yes)) < 0)
854 if (!arg->silent)
855 printf ("failed\n");
856 perror ("setsockopt");
857 close (ls->sockfd);
858 ls->sockfd = 0;
859 continue;
862 if (bind (ls->sockfd, &ls->addr, sizeof (ls->addr)) != 0)
864 if (!arg->silent)
865 printf ("failed\n");
866 perror ("bind");
867 close (ls->sockfd);
868 ls->sockfd = 0;
869 continue;
872 if (ls->type == SOCK_STREAM && listen (ls->sockfd, 512) != 0)
874 if (!arg->silent)
875 printf ("failed\n");
876 perror ("listen");
877 close (ls->sockfd);
878 ls->sockfd = 0;
879 continue;
882 maxfd++;
883 if (!arg->silent)
884 printf ("done\n");
887 if (maxfd == 0)
889 fprintf (stderr, "Failed to bind any ports.\n");
890 return 1;
893 if (!arg->silent)
894 printf ("Listening on %d ports...\n", maxfd);
896 return 0;
899 static int
900 kdc_loop (Shishi * handle, struct arguments *arg)
902 struct listenspec *ls;
903 fd_set readfds;
904 struct sockaddr addr;
905 socklen_t length = sizeof (addr);
906 int maxfd = 0;
907 int rc;
908 int i;
909 ssize_t sent_bytes, read_bytes;
911 while (!quit)
913 sleep (1);
916 FD_ZERO (&readfds);
917 maxfd = 0;
918 for (i = 0; i < arg->nlistenspec; i++)
920 if (arg->listenspec[i].sockfd >= maxfd)
921 maxfd = arg->listenspec[i].sockfd + 1;
922 FD_SET (arg->listenspec[i].sockfd, &readfds);
925 while ((rc = select (maxfd, &readfds, NULL, NULL, NULL)) == 0);
927 if (rc < 0)
929 if (errno != EINTR)
930 perror ("select");
931 continue;
934 for (i = 0; i < arg->nlistenspec; i++)
935 if (FD_ISSET (arg->listenspec[i].sockfd, &readfds))
937 if (arg->listenspec[i].type == SOCK_STREAM &&
938 arg->listenspec[i].family != -1)
940 fprintf (stderr, "New connection on %s...",
941 arg->listenspec[i].str);
943 /* XXX search for closed fd's before allocating new entry */
944 arg->listenspec = realloc (arg->listenspec,
945 sizeof (*arg->listenspec) *
946 (arg->nlistenspec + 1));
947 if (arg->listenspec != NULL)
949 struct sockaddr_in *sin;
950 char *str;
952 arg->nlistenspec++;
953 ls = &arg->listenspec[arg->nlistenspec - 1];
954 ls->bufpos = 0;
955 ls->type = arg->listenspec[i].type;
956 ls->family = -1;
957 length = sizeof (ls->addr);
958 ls->sockfd = accept (arg->listenspec[i].sockfd,
959 &ls->addr, &length);
960 sin = (struct sockaddr_in *) &ls->addr;
961 str = inet_ntoa (sin->sin_addr);
962 ls->str = malloc (strlen (arg->listenspec[i].str) +
963 strlen (" peer ") + strlen (str) + 1);
964 sprintf (ls->str, "%s peer %s", arg->listenspec[i].str,
965 str);
966 puts (ls->str);
969 else
971 ls = &arg->listenspec[i];
973 read_bytes = recvfrom (ls->sockfd, ls->buf + ls->bufpos,
974 BUFSIZ - ls->bufpos, 0, &addr,
975 &length);
977 if (arg->listenspec[i].type == SOCK_STREAM &&
978 arg->listenspec[i].family == -1 && read_bytes == 0)
980 printf ("Peer %s disconnected\n", ls->str);
981 close (ls->sockfd);
982 ls->sockfd = 0;
984 else
986 ls->bufpos += read_bytes;
987 ls->buf[ls->bufpos] = '\0';
990 printf ("Has %d bytes from %s\n", ls->bufpos, ls->str);
992 if (arg->listenspec[i].type == SOCK_DGRAM ||
993 (ls->bufpos > 4 &&
994 ntohl (*(int *) ls->buf) + 4 == ls->bufpos))
996 char *p;
997 size_t plen;
999 if (arg->listenspec[i].type == SOCK_STREAM)
1000 process (handle, arg, ls->buf + 4, ls->bufpos - 4,
1001 &p, &plen);
1002 else
1003 process (handle, arg, ls->buf, ls->bufpos, &p, &plen);
1005 if (p && plen > 0)
1008 sent_bytes = sendto (ls->sockfd, p, plen,
1009 0, &addr, length);
1010 while (sent_bytes == -1 && errno == EAGAIN);
1012 if (sent_bytes < 0)
1013 perror ("write");
1014 else if ((size_t) sent_bytes > plen)
1015 fprintf (stderr, "wrote %db but buffer only %db",
1016 sent_bytes, plen);
1017 else if ((size_t) sent_bytes < plen)
1018 fprintf (stderr,
1019 "short write (%db) writing %d bytes\n",
1020 sent_bytes, plen);
1022 if (p != fatal_krberror)
1023 free (p);
1026 ls->bufpos = 0;
1032 return 0;
1035 static int
1036 kdc_setuid (struct arguments *arg)
1038 struct passwd *passwd;
1039 int rc;
1041 if (!arg->setuid)
1042 return 0;
1044 passwd = getpwnam (arg->setuid);
1045 if (passwd == NULL)
1047 perror ("setuid: getpwnam");
1048 return 1;
1051 rc = setuid (passwd->pw_uid);
1052 if (rc == -1)
1054 perror ("setuid");
1055 return 1;
1058 if (!arg->silent)
1059 printf ("User identity set to `%s' (%d)...\n",
1060 passwd->pw_name, passwd->pw_uid);
1062 return 0;
1065 static void
1066 kdc_unlisten (struct arguments *arg)
1068 int i;
1069 int rc;
1071 for (i = 0; i < arg->nlistenspec; i++)
1072 if (arg->listenspec[i].sockfd)
1074 if (!arg->silent)
1075 printf ("Closing %s...", arg->listenspec[i].str);
1076 rc = close (arg->listenspec[i].sockfd);
1077 if (rc != 0)
1079 if (!arg->silent)
1080 printf ("failed\n");
1081 perror ("close");
1083 else if (!arg->silent)
1084 printf ("done\n");
1088 static int
1089 launch (Shishi * handle, struct arguments *arg)
1091 int rc;
1093 rc = kdc_listen (arg);
1094 if (rc != 0)
1095 return rc;
1097 rc = kdc_setuid (arg);
1098 if (rc != 0)
1099 return rc;
1101 signal (SIGINT, ctrlc);
1102 signal (SIGTERM, ctrlc);
1104 rc = kdc_loop (handle, arg);
1105 if (rc != 0)
1106 return rc;
1108 kdc_unlisten (arg);
1110 return 0;
1113 static int
1114 setup (Shishi * handle, struct arguments *arg)
1116 char *tgtname;
1117 int rc;
1119 rc = setup_fatal_krberror (handle);
1120 if (rc != SHISHI_OK)
1122 syslog (LOG_ERR, "Cannot allocate fatal error message\n");
1123 return 1;
1126 asprintf (&tgtname, "krbtgt/%s", shishi_realm_default (handle));
1127 arg->tgskey = shishi_keys_for_serverrealm_in_file
1128 (handle, arg->keyfile, tgtname, shishi_realm_default (handle));
1129 free (tgtname);
1130 if (!arg->tgskey)
1132 syslog (LOG_ERR, "Key for krbtgt/%s not found in %s\n",
1133 shishi_realm_default (handle), arg->keyfile);
1134 return 1;
1137 rc = launch (handle, arg);
1139 shishi_key_done (arg->tgskey);
1141 return rc;
1144 static int
1145 init (struct arguments *arg)
1147 Shishi *handle;
1148 int rc;
1150 rc = shishi_init_server_with_paths (&handle, arg->cfgfile);
1151 if (rc != SHISHI_OK)
1153 syslog (LOG_ERR, "Aborting due to library initialization failure\n");
1154 return 1;
1157 rc = setup (handle, arg);
1159 shishi_done (handle);
1161 return rc;
1165 main (int argc, char *argv[])
1167 struct arguments arg;
1168 int rc;
1170 #ifdef LOG_PERROR
1171 openlog (PACKAGE, LOG_CONS | LOG_PERROR, LOG_DAEMON);
1172 #else
1173 openlog (PACKAGE, LOG_CONS, LOG_DAEMON);
1174 #endif
1176 memset ((void *) &arg, 0, sizeof (arg));
1177 argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &arg);
1179 if (!arg.keyfile)
1180 arg.keyfile = strdup (KDCKEYFILE);
1182 if (!arg.cfgfile)
1183 arg.cfgfile = strdup (SYSTEMCFGFILE);
1185 rc = init (&arg);
1187 free (arg.keyfile);
1188 free (arg.cfgfile);
1189 if (arg.setuid)
1190 free (arg.setuid);
1192 closelog ();
1194 return rc;