1 /* server.c --- Handle KDC sessions.
2 * Copyright (C) 2002, 2003, 2004, 2006 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 /* Note: only use syslog to report errors in this file. */
24 /* Get Shishid stuff. */
27 /* Accept new TCP connection in a new listenspec entry. */
29 kdc_accept (struct listenspec
*ls
)
31 struct listenspec
*newls
;
33 newls
= xzalloc (sizeof (*newls
));
34 newls
->next
= ls
->next
;
38 newls
->type
= ls
->type
;
39 newls
->addrlen
= sizeof (newls
->addr
);
40 newls
->sockfd
= accept (ls
->sockfd
, &newls
->addr
, &newls
->addrlen
);
41 newls
->sin
= (struct sockaddr_in
*) &newls
->addr
;
42 asprintf (&newls
->str
, "%s peer %s", ls
->str
,
43 inet_ntoa (newls
->sin
->sin_addr
));
45 syslog (LOG_DEBUG
, "Accepted socket %d from socket %d as %s",
46 newls
->sockfd
, ls
->sockfd
, newls
->str
);
49 /* Destroy listenspec element and return pointer to element before the
50 removed element, or NULL if the first element was removed (or the
51 destroyed list element wasn't in the list). */
52 static struct listenspec
*
53 kdc_close (struct listenspec
*ls
)
55 struct listenspec
*tmp
;
58 syslog (LOG_INFO
, "Closing %s socket %d", ls
->str
, ls
->sockfd
);
64 rc
= gnutls_bye (ls
->session
, GNUTLS_SHUT_RDWR
);
65 while (rc
== GNUTLS_E_AGAIN
|| rc
== GNUTLS_E_INTERRUPTED
);
67 if (rc
!= GNUTLS_E_SUCCESS
)
68 syslog (LOG_ERR
, "TLS terminate failed to %s on socket %d (%d): %s",
69 ls
->str
, ls
->sockfd
, rc
, gnutls_strerror (rc
));
71 gnutls_deinit (ls
->session
);
77 rc
= close (ls
->sockfd
);
79 syslog (LOG_ERR
, "Close failed to %s on socket %d (%d): %s",
80 ls
->str
, ls
->sockfd
, rc
, strerror (rc
));
86 for (tmp
= listenspec
; tmp
&& tmp
->next
!= ls
; tmp
= tmp
->next
)
96 /* Send string to peer, via UDP/TCP/TLS, reporting any errors. */
98 kdc_send1 (struct listenspec
*ls
)
105 sent_bytes
= gnutls_record_send (ls
->session
, ls
->buf
, ls
->bufpos
);
108 sent_bytes
= sendto (ls
->sockfd
, ls
->buf
, ls
->bufpos
,
109 0, &ls
->addr
, ls
->addrlen
);
110 while (sent_bytes
== -1 && errno
== EAGAIN
);
113 syslog (LOG_ERR
, "Error writing %d bytes to %s on socket %d: %s",
114 ls
->bufpos
, ls
->str
, ls
->sockfd
, strerror (errno
));
115 else if ((size_t) sent_bytes
> ls
->bufpos
)
116 syslog (LOG_ERR
, "Overlong write (%d > %d) to %s on socket %d",
117 sent_bytes
, ls
->bufpos
, ls
->str
, ls
->sockfd
);
118 else if ((size_t) sent_bytes
< ls
->bufpos
)
119 syslog (LOG_ERR
, "Short write (%d < %d) to %s on socket %d",
120 sent_bytes
, ls
->bufpos
, ls
->str
, ls
->sockfd
);
123 /* Format response and send it to peer, via UDP/TCP/TLS, reporting any
126 kdc_send (struct listenspec
*ls
)
128 if (ls
->type
== SOCK_DGRAM
)
129 syslog (LOG_DEBUG
, "Sending %d bytes to %s socket %d via UDP",
130 ls
->bufpos
, ls
->str
, ls
->sockfd
);
133 syslog (LOG_DEBUG
, "Sending %d bytes to %s socket %d via %s",
134 ls
->bufpos
, ls
->str
, ls
->sockfd
, ls
->usetls
? "TLS" : "TCP");
136 if (ls
->bufpos
+ 4 >= sizeof (ls
->buf
))
137 ls
->bufpos
= sizeof (ls
->buf
) - 4;
139 memmove (ls
->buf
+ 4, ls
->buf
, ls
->bufpos
);
140 ls
->buf
[0] = (ls
->bufpos
>> 24) & 0xFF;
141 ls
->buf
[1] = (ls
->bufpos
>> 16) & 0xFF;
142 ls
->buf
[2] = (ls
->bufpos
>> 8) & 0xFF;
143 ls
->buf
[3] = ls
->bufpos
& 0xFF;
153 /* Dummy function to replace starttls.c functionality. */
155 kdc_extension (struct listenspec
*ls
)
161 /* Read data from peer, reporting any errors. */
163 kdc_read (struct listenspec
*ls
)
167 ls
->addrlen
= sizeof (ls
->addr
);
170 read_bytes
= gnutls_record_recv (ls
->session
,
171 ls
->buf
+ ls
->bufpos
,
172 sizeof (ls
->buf
) - ls
->bufpos
);
175 read_bytes
= recvfrom (ls
->sockfd
, ls
->buf
+ ls
->bufpos
,
176 sizeof (ls
->buf
) - ls
->bufpos
, 0,
177 &ls
->addr
, &ls
->addrlen
);
182 syslog (LOG_ERR
, "Corrupt TLS data from %s on socket %d (%d): %s",
183 ls
->str
, ls
->sockfd
, read_bytes
,
184 gnutls_strerror (read_bytes
));
187 syslog (LOG_ERR
, "Error reading from %s on socket %d (%d): %s",
188 ls
->str
, ls
->sockfd
, read_bytes
, strerror (read_bytes
));
192 if (read_bytes
== 0 && ls
->type
== SOCK_STREAM
)
194 syslog (LOG_DEBUG
, "Peer %s disconnected on socket %d\n",
195 ls
->str
, ls
->sockfd
);
199 ls
->bufpos
+= read_bytes
;
201 syslog (LOG_DEBUG
, "Has %d bytes from %s on socket %d\n",
202 ls
->bufpos
, ls
->str
, ls
->sockfd
);
207 #define C2I(buf) ((buf[3] & 0xFF) | \
208 ((buf[2] & 0xFF) << 8) | \
209 ((buf[1] & 0xFF) << 16) | \
210 ((buf[0] & 0xFF) << 24))
212 /* Have we read an entire request? */
214 kdc_ready (struct listenspec
*ls
)
216 int waitfor
= ls
->bufpos
>= 4 ? C2I (ls
->buf
) : 4;
218 syslog (LOG_DEBUG
, "Got %d bytes of %d bytes from %s on socket %d\n",
219 ls
->bufpos
, waitfor
+ 4, ls
->str
, ls
->sockfd
);
221 if (ls
->type
== SOCK_DGRAM
&& ls
->bufpos
> 0)
223 else if (ls
->bufpos
> 4 && waitfor
+ 4 == ls
->bufpos
)
229 /* Process a request and store reply in same buffer. */
231 kdc_process (struct listenspec
*ls
)
236 syslog (LOG_DEBUG
, "Processing %d from %s on socket %d",
237 ls
->bufpos
, ls
->str
, ls
->sockfd
);
239 if (ls
->type
== SOCK_DGRAM
)
240 plen
= process (ls
->buf
, ls
->bufpos
, &p
);
242 plen
= process (ls
->buf
+ 4, ls
->bufpos
- 4, &p
);
246 syslog (LOG_ERR
, "Processing request failed for %s on socket %d (%d)",
247 ls
->str
, ls
->sockfd
, plen
);
248 memcpy (ls
->buf
, fatal_krberror
, fatal_krberror_len
);
249 ls
->bufpos
= fatal_krberror_len
;
253 memcpy (ls
->buf
, p
, plen
);
258 syslog (LOG_DEBUG
, "Have %d bytes for %s on socket %d",
259 ls
->bufpos
, ls
->str
, ls
->sockfd
);
270 #define MAX(a,b) ((a) > (b) ? (a) : (b))
272 /* Main KDC logic, loop around select and call kdc_accept, kdc_read,
273 kdc_extension, kdc_process and kdc_send. This return when the
274 SIGINT or SIGTERM signals are received. */
278 struct listenspec
*ls
;
283 signal (SIGINT
, ctrlc
);
284 signal (SIGTERM
, ctrlc
);
287 syslog (LOG_DEBUG
, "Starting (GNUTLS `%s')", gnutls_check_version (NULL
));
289 syslog (LOG_DEBUG
, "Starting (no TLS)");
298 for (ls
= listenspec
; ls
; ls
= ls
->next
)
302 maxfd
= MAX (maxfd
, ls
->sockfd
+ 1);
304 syslog (LOG_DEBUG
, "Listening on %s socket %d\n",
305 ls
->str
, ls
->sockfd
);
306 FD_SET (ls
->sockfd
, &readfds
);
310 while ((rc
= select (maxfd
, &readfds
, NULL
, NULL
, NULL
)) == 0);
315 syslog (LOG_ERR
, "Error listening on sockets (%d): %s",
316 rc
, strerror (errno
));
320 for (ls
= listenspec
; ls
; ls
= ls
->next
)
321 if (ls
->sockfd
> 0 && FD_ISSET (ls
->sockfd
, &readfds
))
323 if (ls
->type
== SOCK_STREAM
&& ls
->listening
)
325 else if (kdc_read (ls
) < 0)
327 else if (kdc_extension (ls
) < 0)
329 else if (kdc_ready (ls
))
337 syslog (LOG_DEBUG
, "Shutting down");