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
41 #if defined HAVE_DECL_H_ERRNO && !HAVE_DECL_H_ERRNO
42 /* extern int h_errno; */
49 #ifdef HAVE_SYS_TYPES_H
50 #include <sys/types.h>
53 #ifdef HAVE_SYS_SELECT_H
54 #include <sys/select.h>
57 #ifdef HAVE_SYS_SOCKET_H
58 #include <sys/socket.h>
61 #ifdef HAVE_SYS_IOCTL_H
62 #include <sys/ioctl.h>
70 # include <inttypes.h>
77 #if TIME_WITH_SYS_TIME
78 # include <sys/time.h>
82 # include <sys/time.h>
89 # if !STDC_HEADERS && HAVE_MEMORY_H
102 #ifdef HAVE_NETINET_IN_H
103 #include <netinet/in.h>
105 #ifdef HAVE_NETINET_IN6_H
106 #include <netinet/in6.h>
109 #ifdef HAVE_ARPA_INET_H
110 #include <arpa/inet.h>
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)
127 #define FAMILY_IPV4 "IPv4"
128 #define FAMILY_IPV6 "IPv6"
131 #define LISTEN_DEFAULT FAMILY_IPV4 ":*:kerberos/udp, " \
132 FAMILY_IPV4 ":*:kerberos/tcp, " \
133 FAMILY_IPV6 ":*:kerberos/udp, " \
134 FAMILY_IPV6 ":*:kerberos/tcp"
136 #define LISTEN_DEFAULT "*:kerberos/udp, *:kerberos/tcp"
143 struct sockaddr addr
;
156 struct listenspec
*listenspec
;
160 struct arguments arg
;
162 const char *argp_program_version
= PACKAGE_STRING
;
163 const char *argp_program_bug_address
= PACKAGE_BUGREPORT
;
166 parse_opt (int key
, char *arg
, struct argp_state
*state
)
168 struct arguments
*arguments
= state
->input
;
177 arguments
->silent
= 1;
181 arguments
->verbose
= 1;
185 arguments
->cfgfile
= strdup (arg
);
189 arguments
->setuid
= strdup (arg
);
193 if (arguments
->nlistenspec
> 0)
195 arg
= strdup (LISTEN_DEFAULT
);
199 for (i
= 0; (val
= strtok_r (i
== 0 ? arg
: NULL
, ", \t", &ptrptr
));
202 char *service
, *proto
;
205 struct listenspec
*ls
;
206 struct sockaddr_in
*sin
;
208 struct sockaddr_in6
*sin6
;
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
);
220 sin
= (struct sockaddr_in
*) &ls
->addr
;
222 sin6
= (struct sockaddr_in6
*) &ls
->addr
;
225 proto
= strrchr (val
, '/');
227 argp_error (state
, "Could not find type in listen spec: `%s'",
232 if (strcmp (proto
, "tcp") == 0)
233 ls
->type
= SOCK_STREAM
;
235 ls
->type
= SOCK_DGRAM
;
237 service
= strrchr (val
, ':');
239 argp_error (state
, "Could not find service in listen spec: `%s'",
244 se
= getservbyname (service
, proto
);
246 ls
->port
= ntohs (se
->s_port
);
247 else if (strcmp (service
, "kerberos") == 0)
249 else if (atoi (service
) != 0)
250 ls
->port
= atoi (service
);
252 argp_error (state
, "Unknown service `%s' in listen spec: `%s'",
256 if (ls
->family
== AF_INET6
)
257 sin6
->sin6_port
= htons (ls
->port
);
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
":");
268 else if (strncmp (val
, FAMILY_IPV6
":", strlen (FAMILY_IPV6
":")) ==
271 ls
->family
= AF_INET6
;
272 val
+= strlen (FAMILY_IPV6
":");
276 ls
->family
= AF_INET
;
278 if (strcmp (val
, "*") == 0)
281 if (ls
->family
== AF_INET6
)
282 sin6
->sin6_addr
= in6addr_any
;
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
);
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
);
302 argp_error (state
, "Unknown protocol family (%d) returned "
303 "by gethostbyname(\"%s\"): `%s'", he
->h_addrtype
,
307 argp_error (state
, "Unknown host `%s' in listen spec: `%s'",
314 argp_error (state
, "Too many arguments: `%s'", arg
);
318 return ARGP_ERR_UNKNOWN
;
324 static struct argp_option options
[] = {
326 {"verbose", 'v', 0, 0,
327 "Produce verbose output."},
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
= {
353 "Shishid -- Key Distribution Center network daemon"
357 asreq1 (Shishi
* handle
, Shishi_as
* as
)
360 Shishi_key
*sessionkey
, *sessiontktkey
, *userkey
;
365 char *username
, *servername
, *serverrealm
;
368 tkt
= shishi_as_tkt (as
);
370 return SHISHI_MALLOC_ERROR
;
375 err
= shishi_kdcreq_etype (handle
, shishi_as_req (as
), &etype
, i
);
376 if (err
== SHISHI_OK
&& shishi_cipher_supported_p (etype
))
379 while (err
== SHISHI_OK
);
380 if (err
!= SHISHI_OK
)
383 /* XXX use a "preferred server kdc etype" from shishi instead? */
384 err
= shishi_key_random (handle
, etype
, &sessionkey
);
387 err
= shishi_key_random (handle
, etype
, &sessiontktkey
);
391 err
= shishi_tkt_key_set (tkt
, sessionkey
);
395 buflen
= sizeof (buf
) - 1;
396 err
= shishi_kdcreq_cname_get (handle
, shishi_as_req (as
), buf
, &buflen
);
397 if (err
!= SHISHI_OK
)
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
)
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
)
416 serverrealm
= strdup (buf
);
417 printf ("serverrealm %s\n", serverrealm
);
421 err
= shishi_tkt_clientrealm_set (tkt
, serverrealm
, username
);
425 err
= shishi_tkt_serverrealm_set (tkt
, serverrealm
, servername
);
429 buflen
= sizeof (buf
);
430 err
= shishi_as_derive_salt (handle
, shishi_as_req (as
), shishi_as_rep (as
),
432 if (err
!= SHISHI_OK
)
435 err
= shishi_key_from_string (handle
,
439 buf
, buflen
, NULL
, &userkey
);
440 if (err
!= SHISHI_OK
)
443 err
= shishi_tkt_build (tkt
, sessiontktkey
);
447 err
= shishi_as_rep_build (as
, userkey
);
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
));
466 asreq (Shishi
* handle
, Shishi_asn1 kdcreq
, char **out
, int *outlen
)
471 rc
= shishi_as (handle
, &as
);
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
);
482 shishi_as_req_set (as
, kdcreq
);
484 *out
= malloc (BUFSIZ
);
487 syslog (LOG_ERR
, "Incoming request failed: Cannot allocate memory\n");
488 /* XXX hard coded KRB-ERROR? */
495 rc
= asreq1 (handle
, as
);
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
);
504 rc
= shishi_as_rep_der (as
, *out
, outlen
);
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
);
520 process (Shishi
* handle
, char *in
, int inlen
, char **out
, int *outlen
)
524 fprintf (stderr
, "Processing %d bytes...\n", inlen
);
526 kdcreq
= shishi_der2asn1_asreq (handle
, in
, inlen
);
529 asreq (handle
, kdcreq
, out
, outlen
);
533 kdcreq
= shishi_der2asn1_tgsreq (handle
, in
, inlen
);
536 fprintf (stderr
, "tgs-req...\n");
539 /* XXX hard coded KRB-ERROR? */
546 void ctrlc (int signum
);
555 doit (Shishi
* handle
, struct arguments arg
)
557 struct listenspec
*ls
;
559 struct sockaddr addr
;
560 socklen_t length
= sizeof (addr
);
564 int sent_bytes
, read_bytes
;
567 for (i
= 0; i
< arg
.nlistenspec
; i
++)
569 ls
= &arg
.listenspec
[i
];
571 ls
->sockfd
= socket (ls
->family
, ls
->type
, 0);
582 if (setsockopt (ls
->sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
583 (char *) &yes
, sizeof (yes
)) < 0)
587 perror ("setsockopt");
593 if (bind (ls
->sockfd
, &ls
->addr
, sizeof (ls
->addr
)) != 0)
603 if (ls
->type
== SOCK_STREAM
&& listen (ls
->sockfd
, 512) != 0)
614 printf ("Listening on %s...", ls
->str
);
623 fprintf (stderr
, "Failed to bind any ports.\n");
628 printf ("Listening on %d ports...\n", maxfd
);
632 struct passwd
*passwd
;
634 passwd
= getpwnam (arg
.setuid
);
637 perror ("setuid: getpwnam");
641 rc
= setuid (passwd
->pw_uid
);
649 printf ("User identity set to `%s' (%d)...\n",
650 passwd
->pw_name
, passwd
->pw_uid
);
653 signal (SIGINT
, ctrlc
);
654 signal (SIGTERM
, ctrlc
);
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);
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
;
698 ls
= &arg
.listenspec
[arg
.nlistenspec
- 1];
700 ls
->type
= arg
.listenspec
[i
].type
;
702 length
= sizeof (ls
->addr
);
703 ls
->sockfd
= accept (arg
.listenspec
[i
].sockfd
,
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
,
716 ls
= &arg
.listenspec
[i
];
718 read_bytes
= recvfrom (ls
->sockfd
, ls
->buf
+ ls
->bufpos
,
719 BUFSIZ
- ls
->bufpos
, 0, &addr
,
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
);
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
||
739 && ntohl (*(int *) ls
->buf
) == ls
->bufpos
))
744 process (handle
, ls
->buf
, ls
->bufpos
, &p
, &plen
);
749 sent_bytes
= sendto (ls
->sockfd
, p
, plen
,
751 while (sent_bytes
== -1 && errno
== EAGAIN
);
753 if (sent_bytes
== -1)
755 else if (sent_bytes
> plen
)
756 fprintf (stderr
, "wrote %db but buffer only %db",
758 else if (sent_bytes
< plen
)
760 "short write (%db) writing %d bytes\n",
772 for (i
= 0; i
< arg
.nlistenspec
; i
++)
773 if (arg
.listenspec
[i
].sockfd
)
776 printf ("Closing %s...", arg
.listenspec
[i
].str
);
777 rc
= close (arg
.listenspec
[i
].sockfd
);
784 else if (!arg
.silent
)
792 main (int argc
, char *argv
[])
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
);
804 syslog (LOG_ERR
, "Aborting due to library initialization failure\n");
808 rc
= doit (handle
, arg
);
810 shishi_done (handle
);