Add comment.
[shishi.git] / src / shishid.c
blob4f6aead5f32f0b2609e06d869a66ea5045c9e741
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 #ifdef ENABLE_NLS
118 extern char *_shishi_gettext (const char *str);
119 #define _(String) _shishi_gettext (String)
120 #define gettext_noop(String) String
121 #define N_(String) gettext_noop (String)
122 #endif
124 #include <shishi.h>
125 #include <argp.h>
127 #define FAMILY_IPV4 "IPv4"
128 #define FAMILY_IPV6 "IPv6"
130 #ifdef WITH_IPV6
131 #define LISTEN_DEFAULT FAMILY_IPV4 ":*:kerberos/udp, " \
132 FAMILY_IPV4 ":*:kerberos/tcp, " \
133 FAMILY_IPV6 ":*:kerberos/udp, " \
134 FAMILY_IPV6 ":*:kerberos/tcp"
135 #else
136 #define LISTEN_DEFAULT "*:kerberos/udp, *:kerberos/tcp"
137 #endif
139 struct listenspec
141 char *str;
142 int family;
143 struct sockaddr addr;
144 int port;
145 int type;
146 int sockfd;
147 char buf[BUFSIZ];
148 size_t bufpos;
151 struct arguments
153 int silent, verbose;
154 char *cfgfile;
155 char *setuid;
156 struct listenspec *listenspec;
157 int nlistenspec;
160 struct arguments arg;
162 const char *argp_program_version = PACKAGE_STRING;
163 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
165 static error_t
166 parse_opt (int key, char *arg, struct argp_state *state)
168 struct arguments *arguments = state->input;
169 char *ptrptr;
170 char *val;
171 int i;
173 switch (key)
175 case 'q':
176 case 's':
177 arguments->silent = 1;
178 break;
180 case 'v':
181 arguments->verbose = 1;
182 break;
184 case 'c':
185 arguments->cfgfile = strdup (arg);
186 break;
188 case 'u':
189 arguments->setuid = strdup (arg);
190 break;
192 case ARGP_KEY_END:
193 if (arguments->nlistenspec > 0)
194 break;
195 arg = strdup (LISTEN_DEFAULT);
196 /* fall through */
198 case 'l':
199 for (i = 0; (val = strtok_r (i == 0 ? arg : NULL, ", \t", &ptrptr));
200 i++)
202 char *service, *proto;
203 struct servent *se;
204 struct hostent *he;
205 struct listenspec *ls;
206 struct sockaddr_in *sin;
207 #ifdef WITH_IPV6
208 struct sockaddr_in6 *sin6;
209 #endif
211 arguments->nlistenspec++;
212 arguments->listenspec = realloc (arguments->listenspec,
213 sizeof (*arguments->listenspec) *
214 arguments->nlistenspec);
215 if (arguments->listenspec == NULL)
216 argp_error (state, "Fatal memory allocation error");
217 ls = &arguments->listenspec[arguments->nlistenspec - 1];
218 ls->str = strdup (val);
219 ls->bufpos = 0;
220 sin = (struct sockaddr_in *) &ls->addr;
221 #ifdef WITH_IPV6
222 sin6 = (struct sockaddr_in6 *) &ls->addr;
223 #endif
225 proto = strrchr (val, '/');
226 if (proto == NULL)
227 argp_error (state, "Could not find type in listen spec: `%s'",
228 ls->str);
229 *proto = '\0';
230 proto++;
232 if (strcmp (proto, "tcp") == 0)
233 ls->type = SOCK_STREAM;
234 else
235 ls->type = SOCK_DGRAM;
237 service = strrchr (val, ':');
238 if (service == NULL)
239 argp_error (state, "Could not find service in listen spec: `%s'",
240 ls->str);
241 *service = '\0';
242 service++;
244 se = getservbyname (service, proto);
245 if (se)
246 ls->port = ntohs (se->s_port);
247 else if (strcmp (service, "kerberos") == 0)
248 ls->port = 88;
249 else if (atoi (service) != 0)
250 ls->port = atoi (service);
251 else
252 argp_error (state, "Unknown service `%s' in listen spec: `%s'",
253 service, ls->str);
255 #ifdef WITH_IPV6
256 if (ls->family == AF_INET6)
257 sin6->sin6_port = htons (ls->port);
258 else
259 #endif
260 sin->sin_port = htons (ls->port);
262 if (strncmp (val, FAMILY_IPV4 ":", strlen (FAMILY_IPV4 ":")) == 0)
264 ls->family = AF_INET;
265 val += strlen (FAMILY_IPV4 ":");
267 #ifdef WITH_IPV6
268 else if (strncmp (val, FAMILY_IPV6 ":", strlen (FAMILY_IPV6 ":")) ==
271 ls->family = AF_INET6;
272 val += strlen (FAMILY_IPV6 ":");
274 #endif
275 else
276 ls->family = AF_INET;
278 if (strcmp (val, "*") == 0)
280 #ifdef WITH_IPV6
281 if (ls->family == AF_INET6)
282 sin6->sin6_addr = in6addr_any;
283 else
284 #endif
285 sin->sin_addr.s_addr = htonl (INADDR_ANY);
287 else if ((he = gethostbyname (val)))
289 if (he->h_addrtype == AF_INET)
291 sin->sin_family = AF_INET;
292 memcpy (&sin->sin_addr, he->h_addr_list[0], he->h_length);
294 #ifdef WITH_IPV6
295 else if (he->h_addrtype == AF_INET6)
297 sin6->sin6_family = AF_INET6;
298 memcpy (&sin6->sin6_addr, he->h_addr_list[0], he->h_length);
300 #endif
301 else
302 argp_error (state, "Unknown protocol family (%d) returned "
303 "by gethostbyname(\"%s\"): `%s'", he->h_addrtype,
304 val, ls->str);
306 else
307 argp_error (state, "Unknown host `%s' in listen spec: `%s'",
308 val, ls->str);
311 break;
313 case ARGP_KEY_ARG:
314 argp_error (state, "Too many arguments: `%s'", arg);
315 break;
317 default:
318 return ARGP_ERR_UNKNOWN;
321 return 0;
324 static struct argp_option options[] = {
326 {"verbose", 'v', 0, 0,
327 "Produce verbose output."},
329 {"quiet", 'q', 0, 0,
330 "Don't produce any output."},
332 {"silent", 's', 0, OPTION_ALIAS},
334 {"configuration-file", 'c', "FILE", 0,
335 "Read configuration from file. Default is " SYSTEMCFGFILE "."},
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 "\"."},
343 {"setuid", 'u', "NAME", 0,
344 "After binding socket, set user identity."},
349 static struct argp argp = {
350 options,
351 parse_opt,
352 NULL,
353 "Shishid -- Key Distribution Center network daemon"
356 static int
357 asreq1 (Shishi * handle, Shishi_as * as)
359 Shishi_tkt *tkt;
360 Shishi_key *sessionkey, *sessiontktkey, *userkey;
361 int etype, i;
362 char buf[BUFSIZ];
363 int buflen;
364 int err;
365 char *username, *servername, *serverrealm;
366 char *password;
368 tkt = shishi_as_tkt (as);
369 if (!tkt)
370 return SHISHI_MALLOC_ERROR;
372 i = 1;
375 err = shishi_kdcreq_etype (handle, shishi_as_req (as), &etype, i);
376 if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
377 break;
379 while (err == SHISHI_OK);
380 if (err != SHISHI_OK)
381 return err;
383 /* XXX use a "preferred server kdc etype" from shishi instead? */
384 err = shishi_key_random (handle, etype, &sessionkey);
385 if (err)
386 return err;
387 err = shishi_key_random (handle, etype, &sessiontktkey);
388 if (err)
389 return err;
391 err = shishi_tkt_key_set (tkt, sessionkey);
392 if (err)
393 return err;
395 buflen = sizeof (buf) - 1;
396 err = shishi_kdcreq_cname_get (handle, shishi_as_req (as), buf, &buflen);
397 if (err != SHISHI_OK)
398 return err;
399 buf[buflen] = '\0';
400 username = strdup (buf);
401 printf ("username %s\n", username);
403 buflen = sizeof (buf) - 1;
404 err = shishi_kdcreq_sname_get (handle, shishi_as_req (as), buf, &buflen);
405 if (err != SHISHI_OK)
406 return err;
407 buf[buflen] = '\0';
408 servername = strdup (buf);
409 printf ("servername %s\n", servername);
411 buflen = sizeof (buf) - 1;
412 err = shishi_kdcreq_realm_get (handle, shishi_as_req (as), buf, &buflen);
413 if (err != SHISHI_OK)
414 return err;
415 buf[buflen] = '\0';
416 serverrealm = strdup (buf);
417 printf ("serverrealm %s\n", serverrealm);
419 password = "foo";
421 err = shishi_tkt_clientrealm_set (tkt, serverrealm, username);
422 if (err)
423 return err;
425 err = shishi_tkt_serverrealm_set (tkt, serverrealm, servername);
426 if (err)
427 return err;
429 buflen = sizeof (buf);
430 err = shishi_as_derive_salt (handle, shishi_as_req (as), shishi_as_rep (as),
431 buf, &buflen);
432 if (err != SHISHI_OK)
433 return err;
435 err = shishi_key_from_string (handle,
436 etype,
437 password,
438 strlen (password),
439 buf, buflen, NULL, &userkey);
440 if (err != SHISHI_OK)
441 return err;
443 err = shishi_tkt_build (tkt, sessiontktkey);
444 if (err)
445 return err;
447 err = shishi_as_rep_build (as, userkey);
448 if (err)
449 return err;
451 if (arg.verbose)
453 shishi_kdcreq_print (handle, stderr, shishi_as_req (as));
454 shishi_encticketpart_print (handle, stderr,
455 shishi_tkt_encticketpart (tkt));
456 shishi_ticket_print (handle, stderr, shishi_tkt_ticket (tkt));
457 shishi_enckdcreppart_print (handle, stderr,
458 shishi_tkt_enckdcreppart (tkt));
459 shishi_kdcrep_print (handle, stderr, shishi_as_rep (as));
462 return SHISHI_OK;
465 static void
466 asreq (Shishi * handle, Shishi_asn1 kdcreq, char **out, int *outlen)
468 Shishi_as *as;
469 int rc;
471 rc = shishi_as (handle, &as);
472 if (rc != SHISHI_OK)
474 syslog (LOG_ERR, "Incoming request failed: Cannot create AS: %s\n",
475 shishi_strerror (rc));
476 /* XXX hard coded KRB-ERROR? */
477 *out = strdup ("foo");
478 *outlen = strlen (*out);
479 return;
482 shishi_as_req_set (as, kdcreq);
484 *out = malloc (BUFSIZ);
485 if (*out == NULL)
487 syslog (LOG_ERR, "Incoming request failed: Cannot allocate memory\n");
488 /* XXX hard coded KRB-ERROR? */
489 *out = NULL;
490 *outlen = 0;
491 return;
493 *outlen = BUFSIZ;
495 rc = asreq1 (handle, as);
496 if (rc != SHISHI_OK)
498 syslog (LOG_NOTICE, "Could not answer request: %s: %s\n",
499 shishi_strerror (rc),
500 shishi_krberror_message (handle, shishi_as_krberror (as)));
501 rc = shishi_as_krberror_der (as, *out, outlen);
503 else
504 rc = shishi_as_rep_der (as, *out, outlen);
505 if (rc != SHISHI_OK)
507 syslog (LOG_ERR,
508 "Incoming request failed: Cannot DER encode reply: %s\n",
509 shishi_strerror (rc));
510 /* XXX hard coded KRB-ERROR? */
511 *out = strdup ("aaaaaa");
512 *outlen = strlen (*out);
513 return;
516 return;
519 static void
520 process (Shishi * handle, char *in, int inlen, char **out, int *outlen)
522 Shishi_asn1 kdcreq;
524 fprintf (stderr, "Processing %d bytes...\n", inlen);
526 kdcreq = shishi_der2asn1_asreq (handle, in, inlen);
527 if (!kdcreq)
529 asreq (handle, kdcreq, out, outlen);
530 return;
533 kdcreq = shishi_der2asn1_tgsreq (handle, in, inlen);
534 if (!kdcreq)
536 fprintf (stderr, "tgs-req...\n");
539 /* XXX hard coded KRB-ERROR? */
540 *out = NULL;
541 *outlen = 0;
544 int quit = 0;
546 void ctrlc (int signum);
548 void
549 ctrlc (int signum)
551 quit = 1;
554 static int
555 doit (Shishi * handle, struct arguments arg)
557 struct listenspec *ls;
558 fd_set readfds;
559 struct sockaddr addr;
560 socklen_t length = sizeof (addr);
561 int maxfd = 0;
562 int rc;
563 int i;
564 int sent_bytes, read_bytes;
565 int yes;
567 for (i = 0; i < arg.nlistenspec; i++)
569 ls = &arg.listenspec[i];
571 ls->sockfd = socket (ls->family, ls->type, 0);
572 if (ls->sockfd < 0)
574 if (!arg.silent)
575 printf ("failed\n");
576 perror ("socket");
577 ls->sockfd = 0;
578 continue;
581 yes = 1;
582 if (setsockopt (ls->sockfd, SOL_SOCKET, SO_REUSEADDR,
583 (char *) &yes, sizeof (yes)) < 0)
585 if (!arg.silent)
586 printf ("failed\n");
587 perror ("setsockopt");
588 close (ls->sockfd);
589 ls->sockfd = 0;
590 continue;
593 if (bind (ls->sockfd, &ls->addr, sizeof (ls->addr)) != 0)
595 if (!arg.silent)
596 printf ("failed\n");
597 perror ("bind");
598 close (ls->sockfd);
599 ls->sockfd = 0;
600 continue;
603 if (ls->type == SOCK_STREAM && listen (ls->sockfd, 512) != 0)
605 if (!arg.silent)
606 printf ("failed\n");
607 perror ("listen");
608 close (ls->sockfd);
609 ls->sockfd = 0;
610 continue;
613 if (!arg.silent)
614 printf ("Listening on %s...", ls->str);
616 maxfd++;
617 if (!arg.silent)
618 printf ("done\n");
621 if (maxfd == 0)
623 fprintf (stderr, "Failed to bind any ports.\n");
624 return 1;
627 if (!arg.silent)
628 printf ("Listening on %d ports...\n", maxfd);
630 if (arg.setuid)
632 struct passwd *passwd;
634 passwd = getpwnam (arg.setuid);
635 if (passwd == NULL)
637 perror ("setuid: getpwnam");
638 return 1;
641 rc = setuid (passwd->pw_uid);
642 if (rc == -1)
644 perror ("setuid");
645 return 1;
648 if (!arg.silent)
649 printf ("User identity set to `%s' (%d)...\n",
650 passwd->pw_name, passwd->pw_uid);
653 signal (SIGINT, ctrlc);
654 signal (SIGTERM, ctrlc);
656 while (!quit)
658 sleep (1);
661 FD_ZERO (&readfds);
662 maxfd = 0;
663 for (i = 0; i < arg.nlistenspec; i++)
665 if (arg.listenspec[i].sockfd >= maxfd)
666 maxfd = arg.listenspec[i].sockfd + 1;
667 FD_SET (arg.listenspec[i].sockfd, &readfds);
670 while ((rc = select (maxfd, &readfds, NULL, NULL, NULL)) == 0);
672 if (rc < 0)
674 if (errno != EINTR)
675 perror ("select");
676 continue;
679 for (i = 0; i < arg.nlistenspec; i++)
680 if (FD_ISSET (arg.listenspec[i].sockfd, &readfds))
682 if (arg.listenspec[i].type == SOCK_STREAM &&
683 arg.listenspec[i].family != -1)
685 fprintf (stderr, "New connection on %s...",
686 arg.listenspec[i].str);
688 /* XXX search for closed fd's before allocating new entry */
689 arg.listenspec = realloc (arg.listenspec,
690 sizeof (*arg.listenspec) *
691 (arg.nlistenspec + 1));
692 if (arg.listenspec != NULL)
694 struct sockaddr_in *sin;
695 char *str;
697 arg.nlistenspec++;
698 ls = &arg.listenspec[arg.nlistenspec - 1];
699 ls->bufpos = 0;
700 ls->type = arg.listenspec[i].type;
701 ls->family = -1;
702 length = sizeof (ls->addr);
703 ls->sockfd = accept (arg.listenspec[i].sockfd,
704 &ls->addr, &length);
705 sin = (struct sockaddr_in *) &ls->addr;
706 str = inet_ntoa (sin->sin_addr);
707 ls->str = malloc (strlen (arg.listenspec[i].str) +
708 strlen (" peer ") + strlen (str) + 1);
709 sprintf (ls->str, "%s peer %s", arg.listenspec[i].str,
710 str);
711 puts (ls->str);
714 else
716 ls = &arg.listenspec[i];
718 read_bytes = recvfrom (ls->sockfd, ls->buf + ls->bufpos,
719 BUFSIZ - ls->bufpos, 0, &addr,
720 &length);
722 if (arg.listenspec[i].type == SOCK_STREAM &&
723 arg.listenspec[i].family == -1 && read_bytes == 0)
725 printf ("Peer %s disconnected\n", ls->str);
726 close (ls->sockfd);
727 ls->sockfd = 0;
729 else
731 ls->bufpos += read_bytes;
732 ls->buf[ls->bufpos] = '\0';
735 printf ("Has %d bytes from %s\n", ls->bufpos, ls->str);
737 if (arg.listenspec[i].type == SOCK_DGRAM ||
738 (ls->bufpos > 4
739 && ntohl (*(int *) ls->buf) == ls->bufpos))
741 char *p;
742 int plen;
744 process (handle, ls->buf, ls->bufpos, &p, &plen);
746 if (p && plen > 0)
749 sent_bytes = sendto (ls->sockfd, p, plen,
750 0, &addr, length);
751 while (sent_bytes == -1 && errno == EAGAIN);
753 if (sent_bytes == -1)
754 perror ("write");
755 else if (sent_bytes > plen)
756 fprintf (stderr, "wrote %db but buffer only %db",
757 sent_bytes, plen);
758 else if (sent_bytes < plen)
759 fprintf (stderr,
760 "short write (%db) writing %d bytes\n",
761 sent_bytes, plen);
763 free (p);
766 ls->bufpos = 0;
772 for (i = 0; i < arg.nlistenspec; i++)
773 if (arg.listenspec[i].sockfd)
775 if (!arg.silent)
776 printf ("Closing %s...", arg.listenspec[i].str);
777 rc = close (arg.listenspec[i].sockfd);
778 if (rc != 0)
780 if (!arg.silent)
781 printf ("failed\n");
782 perror ("close");
784 else if (!arg.silent)
785 printf ("done\n");
788 return 0;
792 main (int argc, char *argv[])
794 Shishi *handle;
795 int rc;
797 openlog (PACKAGE, LOG_PERROR, LOG_AUTHPRIV);
798 memset ((void *) &arg, 0, sizeof (arg));
799 argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &arg);
801 rc = shishi_init_server_with_paths (&handle, arg.cfgfile);
802 if (rc != SHISHI_OK)
804 syslog (LOG_ERR, "Aborting due to library initialization failure\n");
805 return 1;
808 rc = doit (handle, arg);
810 shishi_done (handle);
811 closelog ();
813 return rc;