1 /* shishid.c --- Shishi Key Distribution Center daemon.
2 * Copyright (C) 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 /* Get Shishid stuff. */
25 /* Get program_name, for error. */
31 /* Global variables. */
34 struct gengetopt_args_info arg
;
35 struct listenspec
*listenspec
;
37 size_t fatal_krberror_len
;
39 gnutls_dh_params dh_params
;
40 gnutls_anon_server_credentials anoncred
;
41 gnutls_certificate_credentials x509cred
;
44 /* Listen to all listenspec's, removing entries that fail. */
48 struct listenspec
*ls
, *tmp
;
52 for (ls
= listenspec
; ls
; ls
= ls
->next
)
55 printf ("Listening on %s...\n", ls
->str
);
57 ls
->sockfd
= socket (ls
->family
, ls
->type
, 0);
60 error (0, errno
, "Cannot listen on %s because socket failed",
66 if (setsockopt (ls
->sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
67 (char *) &yes
, sizeof (yes
)) < 0)
69 error (0, errno
, "Cannot listen on %s because setsockopt failed",
74 if (bind (ls
->sockfd
, &ls
->listenaddr
, sizeof (ls
->listenaddr
)) != 0)
76 error (0, errno
, "Cannot listen on %s because bind failed",
81 if (ls
->type
== SOCK_STREAM
&& listen (ls
->sockfd
, SOMAXCONN
) != 0)
83 error (0, errno
, "Cannot listen on %s because listen failed",
105 error (EXIT_FAILURE
, 0, "Failed to bind any ports.");
108 printf ("Listening on %d ports...\n", maxfd
);
111 /* Close open sockets, reporting any errors. */
115 struct listenspec
*ls
;
118 for (ls
= listenspec
; ls
; ls
= ls
->next
)
122 "Closing outstanding connection to %s on socket %d",
123 ls
->str
, ls
->sockfd
);
128 printf ("Closing %s...\n", ls
->str
);
129 rc
= close (ls
->sockfd
);
131 syslog (LOG_ERR
, "Could not close %s on socket %d: %s (%d)",
132 ls
->str
, ls
->sockfd
, strerror (errno
), errno
);
140 /* If requested, abandon user privileges. */
144 struct passwd
*passwd
;
147 if (!arg
.setuid_given
)
150 passwd
= getpwnam (arg
.setuid_arg
);
154 error (EXIT_FAILURE
, errno
, "Cannot setuid because getpwnam failed");
156 error (EXIT_FAILURE
, 0, "No such user `%s'.", arg
.setuid_arg
);
159 rc
= setuid (passwd
->pw_uid
);
161 error (EXIT_FAILURE
, errno
, "Cannot setuid");
164 printf ("User identity set to `%s' (%d)...\n",
165 passwd
->pw_name
, passwd
->pw_uid
);
168 /* Create a hard coded error message that can be used in case kdc.c
171 setup_fatal_krberror (void)
176 krberr
= shishi_krberror (handle
);
178 return SHISHI_MALLOC_ERROR
;
180 rc
= shishi_krberror_set_etext (handle
, krberr
,
181 "Internal KDC error, contact administrator");
185 rc
= shishi_krberror_der (handle
, krberr
, &fatal_krberror
,
186 &fatal_krberror_len
);
193 /* Core daemon part. Initialize and set up various things, and then
194 hand over control to kdc.c via kdc_loop, and cleaning up
195 afterwards. Note that kdc_loop only return when the process has
196 received SIGINT or SIGTERM. */
202 rc
= shishi_init_server_with_paths (&handle
, arg
.configuration_file_arg
);
204 error (EXIT_FAILURE
, 0, "Cannot initialize Shishi: %s (%d)",
205 shishi_strerror (rc
), rc
);
207 if (arg
.verbose_given
> 1)
208 shishi_cfg (handle
, "verbose");
209 if (arg
.verbose_given
> 2)
210 shishi_cfg (handle
, "verbose-noise");
211 if (arg
.verbose_given
> 3)
212 shishi_cfg (handle
, "verbose-asn1");
213 if (arg
.verbose_given
> 4)
214 shishi_cfg (handle
, "verbose-crypto");
215 if (arg
.verbose_given
> 5)
216 shishi_cfg (handle
, "verbose-crypto-noise");
218 rc
= shisa_init (&dbh
);
220 error (EXIT_FAILURE
, 0, "Cannot initialize Shisa: %s (%d)",
221 shisa_strerror (rc
), rc
);
223 rc
= setup_fatal_krberror ();
225 error (EXIT_FAILURE
, 0, "Cannot allocate fatal error packet: %s (%d)",
226 shisa_strerror (rc
), rc
);
230 printf ("Initializing GNUTLS...\n");
232 rc
= gnutls_global_init ();
234 error (EXIT_FAILURE
, 0, "Cannot initialize GNUTLS: %s (%d)",
235 gnutls_strerror (rc
), rc
);
237 rc
= gnutls_anon_allocate_server_credentials (&anoncred
);
239 error (EXIT_FAILURE
, 0, "Cannot allocate GNUTLS credential: %s (%d)",
240 gnutls_strerror (rc
), rc
);
242 rc
= gnutls_certificate_allocate_credentials (&x509cred
);
244 error (EXIT_FAILURE
, 0,
245 "Cannot allocate GNUTLS X.509 credential: %s (%d)",
246 gnutls_strerror (rc
), rc
);
248 if (arg
.x509cafile_given
)
251 num
= gnutls_certificate_set_x509_trust_file (x509cred
,
253 GNUTLS_X509_FMT_PEM
);
255 error (EXIT_FAILURE
, 0, "No X.509 CAs found in `%s' (%d): %s",
256 arg
.x509cafile_arg
, num
, gnutls_strerror (num
));
258 printf ("Parsed %d CAs...\n", num
);
261 if (arg
.x509crlfile_given
)
265 num
= gnutls_certificate_set_x509_crl_file (x509cred
,
267 GNUTLS_X509_FMT_PEM
);
269 error (EXIT_FAILURE
, 0, "No X.509 CRLs found in `%s' (%d): %s",
270 arg
.x509crlfile_arg
, num
, gnutls_strerror (num
));
272 printf ("Parsed %d CRLs...\n", num
);
275 if (arg
.x509certfile_given
&& arg
.x509keyfile_given
)
277 rc
= gnutls_certificate_set_x509_key_file (x509cred
,
278 arg
.x509certfile_arg
,
280 GNUTLS_X509_FMT_PEM
);
281 if (rc
!= GNUTLS_E_SUCCESS
)
282 error (EXIT_FAILURE
, 0,
283 "No X.509 server certificate/key found in `%s'/`%s' (%d): %s",
284 arg
.x509certfile_arg
, arg
.x509keyfile_arg
, rc
,
285 gnutls_strerror (rc
));
287 printf ("Loaded server certificate/key...\n");
289 else if (arg
.x509certfile_given
|| arg
.x509keyfile_given
)
290 error (EXIT_FAILURE
, 0, "Need both --x509certfile and --x509keyfile");
292 rc
= gnutls_dh_params_init (&dh_params
);
294 error (EXIT_FAILURE
, 0, "Cannot initialize GNUTLS DH parameters: %s (%d)",
295 gnutls_strerror (rc
), rc
);
298 printf ("Generating Diffie-Hellman parameters...\n");
300 rc
= gnutls_dh_params_generate2 (dh_params
, DH_BITS
);
302 error (EXIT_FAILURE
, 0, "Cannot generate GNUTLS DH parameters: %s (%d)",
303 gnutls_strerror (rc
), rc
);
305 gnutls_anon_set_server_dh_params (anoncred
, dh_params
);
307 gnutls_certificate_set_dh_params (x509cred
, dh_params
);
309 resume_db_init (arg
.resume_limit_arg
);
312 printf ("Initializing GNUTLS...done\n");
318 char *slash
= strrchr (program_name
, '/');
319 char *shortname
= (slash
!= NULL
? slash
+ 1 : program_name
);
322 openlog (shortname
, LOG_CONS
| LOG_PERROR
, LOG_DAEMON
);
324 openlog (shortname
, LOG_CONS
, LOG_DAEMON
);
336 printf ("Deinitializing GNUTLS...\n");
340 gnutls_global_deinit ();
343 printf ("Deinitializing GNUTLS...done\n");
347 shishi_done (handle
);
350 #define FAMILY_IPV4 "IPv4"
351 #define FAMILY_IPV6 "IPv6"
354 # define LISTEN_DEFAULT FAMILY_IPV4 ":*:kerberos/udp, " \
355 FAMILY_IPV4 ":*:kerberos/tcp, " \
356 FAMILY_IPV6 ":*:kerberos/udp, " \
357 FAMILY_IPV6 ":*:kerberos/tcp"
359 # define LISTEN_DEFAULT "*:kerberos/udp, *:kerberos/tcp"
362 /* Parse the --listen parameter, creating listenspec elements. */
364 parse_listen (char *listenstr
)
370 for (i
= 0; (val
= strtok_r (i
== 0 ? listenstr
: NULL
,
371 ", \t", &ptrptr
)); i
++)
373 char *service
, *proto
;
376 struct listenspec
*ls
;
377 struct sockaddr_in
*sockin
;
379 struct sockaddr_in6
*sockin6
;
382 ls
= xzalloc (sizeof (*ls
));
383 ls
->next
= listenspec
;
386 ls
->str
= strdup (val
);
389 sockin
= (struct sockaddr_in
*) &ls
->listenaddr
;
391 sockin6
= (struct sockaddr_in6
*) &ls
->listenaddr
;
394 proto
= strrchr (val
, '/');
396 error (EXIT_FAILURE
, 0, "Could not find type in listen spec: `%s'",
401 if (strcmp (proto
, "tcp") == 0)
402 ls
->type
= SOCK_STREAM
;
404 ls
->type
= SOCK_DGRAM
;
406 service
= strrchr (val
, ':');
408 error (EXIT_FAILURE
, 0, "Could not find service in listen spec: `%s'",
413 se
= getservbyname (service
, proto
);
415 ls
->port
= ntohs (se
->s_port
);
416 else if (strcmp (service
, "kerberos") == 0)
418 else if (atoi (service
) != 0)
419 ls
->port
= atoi (service
);
421 error (EXIT_FAILURE
, 0, "Unknown service `%s' in listen spec: `%s'",
425 if (ls
->family
== AF_INET6
)
426 sockin6
->sin6_port
= htons (ls
->port
);
429 sockin
->sin_port
= htons (ls
->port
);
431 if (strncmp (val
, FAMILY_IPV4
":", strlen (FAMILY_IPV4
":")) == 0)
433 ls
->family
= AF_INET
;
434 val
+= strlen (FAMILY_IPV4
":");
437 else if (strncmp (val
, FAMILY_IPV6
":", strlen (FAMILY_IPV6
":")) == 0)
439 ls
->family
= AF_INET6
;
440 val
+= strlen (FAMILY_IPV6
":");
444 ls
->family
= AF_INET
;
446 if (strcmp (val
, "*") == 0)
449 if (ls
->family
== AF_INET6
)
450 sockin6
->sin6_addr
= in6addr_any
;
453 sockin
->sin_addr
.s_addr
= htonl (INADDR_ANY
);
455 else if ((he
= gethostbyname (val
)))
457 if (he
->h_addrtype
== AF_INET
)
459 sockin
->sin_family
= AF_INET
;
460 memcpy (&sockin
->sin_addr
, he
->h_addr_list
[0], he
->h_length
);
463 else if (he
->h_addrtype
== AF_INET6
)
465 sockin6
->sin6_family
= AF_INET6
;
466 memcpy (&sockin6
->sin6_addr
, he
->h_addr_list
[0], he
->h_length
);
470 error (EXIT_FAILURE
, 0, "Unknown protocol family (%d) returned "
471 "by gethostbyname(\"%s\"): `%s'", he
->h_addrtype
,
475 error (EXIT_FAILURE
, 0, "Unknown host `%s' in listen spec: `%s'",
481 main (int argc
, char *argv
[])
483 setlocale (LC_ALL
, "");
484 bindtextdomain (PACKAGE
, LOCALEDIR
);
485 textdomain (PACKAGE
);
486 set_program_name (argv
[0]);
488 if (cmdline_parser (argc
, argv
, &arg
) != 0)
489 error (EXIT_FAILURE
, 0, "Try `%s --help' for more information.", argv
[0]);
493 cmdline_parser_print_help ();
494 printf ("\nMandatory arguments to long options are "
495 "mandatory for short options too.\n\n");
496 printf ("Report bugs to <%s>.\n", PACKAGE_BUGREPORT
);
500 if (!arg
.configuration_file_arg
)
501 arg
.configuration_file_arg
= strdup (SYSTEMCFGFILE
);
502 if (!arg
.listen_given
)
503 arg
.listen_arg
= strdup (LISTEN_DEFAULT
);
504 parse_listen (arg
.listen_arg
);
508 free (arg
.listen_arg
);
509 free (arg
.configuration_file_arg
);
511 free (arg
.setuid_arg
);