Try again to work around line numbers in *.po.
[shishi.git] / src / shishid.c
blob26a6bda84acb8a1942d396c11031030fb4810170
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. */
23 #include "kdc.h"
25 /* Get program_name, for error. */
26 #include "progname.h"
28 /* Get error. */
29 #include "error.h"
31 /* Global variables. */
32 Shishi *handle;
33 Shisa *dbh;
34 struct gengetopt_args_info arg;
35 struct listenspec *listenspec;
36 char *fatal_krberror;
37 size_t fatal_krberror_len;
38 #ifdef USE_STARTTLS
39 gnutls_dh_params dh_params;
40 gnutls_anon_server_credentials anoncred;
41 gnutls_certificate_credentials x509cred;
42 #endif
44 /* Listen to all listenspec's, removing entries that fail. */
45 static void
46 kdc_listen (void)
48 struct listenspec *ls, *tmp;
49 int maxfd = 0;
50 int yes;
52 for (ls = listenspec; ls; ls = ls->next)
54 if (!arg.quiet_flag)
55 printf ("Listening on %s...\n", ls->str);
57 ls->sockfd = socket (ls->family, ls->type, 0);
58 if (ls->sockfd < 0)
60 error (0, errno, "Cannot listen on %s because socket failed",
61 ls->str);
62 goto error;
65 yes = 1;
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",
70 ls->str);
71 goto errorclose;
74 if (bind (ls->sockfd, &ls->listenaddr, sizeof (ls->listenaddr)) != 0)
76 error (0, errno, "Cannot listen on %s because bind failed",
77 ls->str);
78 goto errorclose;
81 if (ls->type == SOCK_STREAM && listen (ls->sockfd, SOMAXCONN) != 0)
83 error (0, errno, "Cannot listen on %s because listen failed",
84 ls->str);
85 goto errorclose;
88 maxfd++;
89 continue;
91 errorclose:
92 close (ls->sockfd);
93 error:
94 tmp = ls->next;
95 if (listenspec == ls)
96 listenspec = tmp;
97 free (ls->str);
98 free (ls);
99 ls = tmp;
100 if (!ls)
101 break;
104 if (maxfd == 0)
105 error (EXIT_FAILURE, 0, "Failed to bind any ports.");
107 if (!arg.quiet_flag)
108 printf ("Listening on %d ports...\n", maxfd);
111 /* Close open sockets, reporting any errors. */
112 static void
113 kdc_unlisten (void)
115 struct listenspec *ls;
116 int rc;
118 for (ls = listenspec; ls; ls = ls->next)
120 if (!ls->listening)
121 syslog (LOG_NOTICE,
122 "Closing outstanding connection to %s on socket %d",
123 ls->str, ls->sockfd);
125 if (ls->sockfd)
127 if (!arg.quiet_flag)
128 printf ("Closing %s...\n", ls->str);
129 rc = close (ls->sockfd);
130 if (rc != 0)
131 syslog (LOG_ERR, "Could not close %s on socket %d: %s (%d)",
132 ls->str, ls->sockfd, strerror (errno), errno);
135 if (ls->str)
136 free (ls->str);
140 /* If requested, abandon user privileges. */
141 static void
142 kdc_setuid (void)
144 struct passwd *passwd;
145 int rc;
147 if (!arg.setuid_given)
148 return;
150 passwd = getpwnam (arg.setuid_arg);
151 if (passwd == NULL)
153 if (errno)
154 error (EXIT_FAILURE, errno, "Cannot setuid because getpwnam failed");
155 else
156 error (EXIT_FAILURE, 0, "No such user `%s'.", arg.setuid_arg);
159 rc = setuid (passwd->pw_uid);
160 if (rc == -1)
161 error (EXIT_FAILURE, errno, "Cannot setuid");
163 if (!arg.quiet_flag)
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
169 fail to produce */
170 static int
171 setup_fatal_krberror (void)
173 Shishi_asn1 krberr;
174 int rc;
176 krberr = shishi_krberror (handle);
177 if (!krberr)
178 return SHISHI_MALLOC_ERROR;
180 rc = shishi_krberror_set_etext (handle, krberr,
181 "Internal KDC error, contact administrator");
182 if (rc != SHISHI_OK)
183 return rc;
185 rc = shishi_krberror_der (handle, krberr, &fatal_krberror,
186 &fatal_krberror_len);
187 if (rc != SHISHI_OK)
188 return rc;
190 return SHISHI_OK;
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. */
197 static void
198 doit (void)
200 int rc;
202 rc = shishi_init_server_with_paths (&handle, arg.configuration_file_arg);
203 if (rc)
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);
219 if (rc)
220 error (EXIT_FAILURE, 0, "Cannot initialize Shisa: %s (%d)",
221 shisa_strerror (rc), rc);
223 rc = setup_fatal_krberror ();
224 if (rc)
225 error (EXIT_FAILURE, 0, "Cannot allocate fatal error packet: %s (%d)",
226 shisa_strerror (rc), rc);
228 #ifdef USE_STARTTLS
229 if (!arg.quiet_flag)
230 printf ("Initializing GNUTLS...\n");
232 rc = gnutls_global_init ();
233 if (rc)
234 error (EXIT_FAILURE, 0, "Cannot initialize GNUTLS: %s (%d)",
235 gnutls_strerror (rc), rc);
237 rc = gnutls_anon_allocate_server_credentials (&anoncred);
238 if (rc)
239 error (EXIT_FAILURE, 0, "Cannot allocate GNUTLS credential: %s (%d)",
240 gnutls_strerror (rc), rc);
242 rc = gnutls_certificate_allocate_credentials (&x509cred);
243 if (rc)
244 error (EXIT_FAILURE, 0,
245 "Cannot allocate GNUTLS X.509 credential: %s (%d)",
246 gnutls_strerror (rc), rc);
248 if (arg.x509cafile_given)
250 int num;
251 num = gnutls_certificate_set_x509_trust_file (x509cred,
252 arg.x509cafile_arg,
253 GNUTLS_X509_FMT_PEM);
254 if (num <= 0)
255 error (EXIT_FAILURE, 0, "No X.509 CAs found in `%s' (%d): %s",
256 arg.x509cafile_arg, num, gnutls_strerror (num));
257 if (!arg.quiet_flag)
258 printf ("Parsed %d CAs...\n", num);
261 if (arg.x509crlfile_given)
263 int num;
265 num = gnutls_certificate_set_x509_crl_file (x509cred,
266 arg.x509crlfile_arg,
267 GNUTLS_X509_FMT_PEM);
268 if (num <= 0)
269 error (EXIT_FAILURE, 0, "No X.509 CRLs found in `%s' (%d): %s",
270 arg.x509crlfile_arg, num, gnutls_strerror (num));
271 if (!arg.quiet_flag)
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,
279 arg.x509keyfile_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));
286 if (!arg.quiet_flag)
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);
293 if (rc)
294 error (EXIT_FAILURE, 0, "Cannot initialize GNUTLS DH parameters: %s (%d)",
295 gnutls_strerror (rc), rc);
297 if (!arg.quiet_flag)
298 printf ("Generating Diffie-Hellman parameters...\n");
300 rc = gnutls_dh_params_generate2 (dh_params, DH_BITS);
301 if (rc)
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);
311 if (!arg.quiet_flag)
312 printf ("Initializing GNUTLS...done\n");
313 #endif
315 kdc_listen ();
318 char *slash = strrchr (program_name, '/');
319 char *shortname = (slash != NULL ? slash + 1 : program_name);
321 #ifdef LOG_PERROR
322 openlog (shortname, LOG_CONS | LOG_PERROR, LOG_DAEMON);
323 #else
324 openlog (shortname, LOG_CONS, LOG_DAEMON);
325 #endif
328 kdc_setuid ();
330 kdc_loop ();
332 kdc_unlisten ();
334 #ifdef USE_STARTTLS
335 if (!arg.quiet_flag)
336 printf ("Deinitializing GNUTLS...\n");
338 resume_db_done ();
340 gnutls_global_deinit ();
342 if (!arg.quiet_flag)
343 printf ("Deinitializing GNUTLS...done\n");
344 #endif
346 shisa_done (dbh);
347 shishi_done (handle);
350 #define FAMILY_IPV4 "IPv4"
351 #define FAMILY_IPV6 "IPv6"
353 #ifdef WITH_IPV6
354 # define LISTEN_DEFAULT FAMILY_IPV4 ":*:kerberos/udp, " \
355 FAMILY_IPV4 ":*:kerberos/tcp, " \
356 FAMILY_IPV6 ":*:kerberos/udp, " \
357 FAMILY_IPV6 ":*:kerberos/tcp"
358 #else
359 # define LISTEN_DEFAULT "*:kerberos/udp, *:kerberos/tcp"
360 #endif
362 /* Parse the --listen parameter, creating listenspec elements. */
363 static void
364 parse_listen (char *listenstr)
366 char *ptrptr;
367 char *val;
368 int i;
370 for (i = 0; (val = strtok_r (i == 0 ? listenstr : NULL,
371 ", \t", &ptrptr)); i++)
373 char *service, *proto;
374 struct servent *se;
375 struct hostent *he;
376 struct listenspec *ls;
377 struct sockaddr_in *sockin;
378 #ifdef WITH_IPV6
379 struct sockaddr_in6 *sockin6;
380 #endif
382 ls = xzalloc (sizeof (*ls));
383 ls->next = listenspec;
384 listenspec = ls;
386 ls->str = strdup (val);
387 ls->bufpos = 0;
388 ls->listening = 1;
389 sockin = (struct sockaddr_in *) &ls->listenaddr;
390 #ifdef WITH_IPV6
391 sockin6 = (struct sockaddr_in6 *) &ls->listenaddr;
392 #endif
394 proto = strrchr (val, '/');
395 if (proto == NULL)
396 error (EXIT_FAILURE, 0, "Could not find type in listen spec: `%s'",
397 ls->str);
398 *proto = '\0';
399 proto++;
401 if (strcmp (proto, "tcp") == 0)
402 ls->type = SOCK_STREAM;
403 else
404 ls->type = SOCK_DGRAM;
406 service = strrchr (val, ':');
407 if (service == NULL)
408 error (EXIT_FAILURE, 0, "Could not find service in listen spec: `%s'",
409 ls->str);
410 *service = '\0';
411 service++;
413 se = getservbyname (service, proto);
414 if (se)
415 ls->port = ntohs (se->s_port);
416 else if (strcmp (service, "kerberos") == 0)
417 ls->port = 88;
418 else if (atoi (service) != 0)
419 ls->port = atoi (service);
420 else
421 error (EXIT_FAILURE, 0, "Unknown service `%s' in listen spec: `%s'",
422 service, ls->str);
424 #ifdef WITH_IPV6
425 if (ls->family == AF_INET6)
426 sockin6->sin6_port = htons (ls->port);
427 else
428 #endif
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 ":");
436 #ifdef WITH_IPV6
437 else if (strncmp (val, FAMILY_IPV6 ":", strlen (FAMILY_IPV6 ":")) == 0)
439 ls->family = AF_INET6;
440 val += strlen (FAMILY_IPV6 ":");
442 #endif
443 else
444 ls->family = AF_INET;
446 if (strcmp (val, "*") == 0)
448 #ifdef WITH_IPV6
449 if (ls->family == AF_INET6)
450 sockin6->sin6_addr = in6addr_any;
451 else
452 #endif
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);
462 #ifdef WITH_IPV6
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);
468 #endif
469 else
470 error (EXIT_FAILURE, 0, "Unknown protocol family (%d) returned "
471 "by gethostbyname(\"%s\"): `%s'", he->h_addrtype,
472 val, ls->str);
474 else
475 error (EXIT_FAILURE, 0, "Unknown host `%s' in listen spec: `%s'",
476 val, ls->str);
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]);
491 if (arg.help_given)
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);
497 return EXIT_SUCCESS;
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);
506 doit ();
508 free (arg.listen_arg);
509 free (arg.configuration_file_arg);
510 if (arg.setuid_arg)
511 free (arg.setuid_arg);
513 return EXIT_SUCCESS;