1 /* server.c --- Handle KDC sessions.
2 * Copyright (C) 2002, 2003, 2004, 2006, 2007 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 3 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, see http://www.gnu.org/licenses or write
18 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
19 * Floor, Boston, MA 02110-1301, USA
23 /* Note: only use syslog to report errors in this file. */
25 /* Get Shishid stuff. */
28 /* Accept new TCP connection in a new listenspec entry. */
30 kdc_accept (struct listenspec
*ls
)
32 struct listenspec
*newls
;
34 newls
= xzalloc (sizeof (*newls
));
35 newls
->next
= ls
->next
;
39 newls
->type
= ls
->type
;
40 newls
->addrlen
= sizeof (newls
->addr
);
41 newls
->sockfd
= accept (ls
->sockfd
, &newls
->addr
, &newls
->addrlen
);
42 newls
->sin
= (struct sockaddr_in
*) &newls
->addr
;
43 asprintf (&newls
->str
, "%s peer %s", ls
->str
,
44 inet_ntoa (newls
->sin
->sin_addr
));
46 syslog (LOG_DEBUG
, "Accepted socket %d from socket %d as %s",
47 newls
->sockfd
, ls
->sockfd
, newls
->str
);
50 /* Destroy listenspec element and return pointer to element before the
51 removed element, or NULL if the first element was removed (or the
52 destroyed list element wasn't in the list). */
53 static struct listenspec
*
54 kdc_close (struct listenspec
*ls
)
56 struct listenspec
*tmp
;
59 syslog (LOG_INFO
, "Closing %s socket %d", ls
->str
, ls
->sockfd
);
65 rc
= gnutls_bye (ls
->session
, GNUTLS_SHUT_RDWR
);
66 while (rc
== GNUTLS_E_AGAIN
|| rc
== GNUTLS_E_INTERRUPTED
);
68 if (rc
!= GNUTLS_E_SUCCESS
)
69 syslog (LOG_ERR
, "TLS terminate failed to %s on socket %d (%d): %s",
70 ls
->str
, ls
->sockfd
, rc
, gnutls_strerror (rc
));
72 gnutls_deinit (ls
->session
);
78 rc
= close (ls
->sockfd
);
80 syslog (LOG_ERR
, "Close failed to %s on socket %d (%d): %s",
81 ls
->str
, ls
->sockfd
, rc
, strerror (rc
));
87 for (tmp
= listenspec
; tmp
&& tmp
->next
!= ls
; tmp
= tmp
->next
)
97 /* Send string to peer, via UDP/TCP/TLS, reporting any errors. */
99 kdc_send1 (struct listenspec
*ls
)
106 sent_bytes
= gnutls_record_send (ls
->session
, ls
->buf
, ls
->bufpos
);
109 sent_bytes
= sendto (ls
->sockfd
, ls
->buf
, ls
->bufpos
,
110 0, &ls
->addr
, ls
->addrlen
);
111 while (sent_bytes
== -1 && errno
== EAGAIN
);
114 syslog (LOG_ERR
, "Error writing %d bytes to %s on socket %d: %s",
115 ls
->bufpos
, ls
->str
, ls
->sockfd
, strerror (errno
));
116 else if ((size_t) sent_bytes
> ls
->bufpos
)
117 syslog (LOG_ERR
, "Overlong write (%d > %d) to %s on socket %d",
118 sent_bytes
, ls
->bufpos
, ls
->str
, ls
->sockfd
);
119 else if ((size_t) sent_bytes
< ls
->bufpos
)
120 syslog (LOG_ERR
, "Short write (%d < %d) to %s on socket %d",
121 sent_bytes
, ls
->bufpos
, ls
->str
, ls
->sockfd
);
124 /* Format response and send it to peer, via UDP/TCP/TLS, reporting any
127 kdc_send (struct listenspec
*ls
)
129 if (ls
->type
== SOCK_DGRAM
)
130 syslog (LOG_DEBUG
, "Sending %d bytes to %s socket %d via UDP",
131 ls
->bufpos
, ls
->str
, ls
->sockfd
);
134 syslog (LOG_DEBUG
, "Sending %d bytes to %s socket %d via %s",
135 ls
->bufpos
, ls
->str
, ls
->sockfd
, ls
->usetls
? "TLS" : "TCP");
137 if (ls
->bufpos
+ 4 >= sizeof (ls
->buf
))
138 ls
->bufpos
= sizeof (ls
->buf
) - 4;
140 memmove (ls
->buf
+ 4, ls
->buf
, ls
->bufpos
);
141 ls
->buf
[0] = (ls
->bufpos
>> 24) & 0xFF;
142 ls
->buf
[1] = (ls
->bufpos
>> 16) & 0xFF;
143 ls
->buf
[2] = (ls
->bufpos
>> 8) & 0xFF;
144 ls
->buf
[3] = ls
->bufpos
& 0xFF;
154 /* Dummy function to replace starttls.c functionality. */
156 kdc_extension (struct listenspec
*ls
)
162 /* Read data from peer, reporting any errors. */
164 kdc_read (struct listenspec
*ls
)
168 ls
->addrlen
= sizeof (ls
->addr
);
171 read_bytes
= gnutls_record_recv (ls
->session
,
172 ls
->buf
+ ls
->bufpos
,
173 sizeof (ls
->buf
) - ls
->bufpos
);
176 read_bytes
= recvfrom (ls
->sockfd
, ls
->buf
+ ls
->bufpos
,
177 sizeof (ls
->buf
) - ls
->bufpos
, 0,
178 &ls
->addr
, &ls
->addrlen
);
183 syslog (LOG_ERR
, "Corrupt TLS data from %s on socket %d (%d): %s",
184 ls
->str
, ls
->sockfd
, read_bytes
,
185 gnutls_strerror (read_bytes
));
188 syslog (LOG_ERR
, "Error reading from %s on socket %d (%d): %s",
189 ls
->str
, ls
->sockfd
, read_bytes
, strerror (read_bytes
));
193 if (read_bytes
== 0 && ls
->type
== SOCK_STREAM
)
195 syslog (LOG_DEBUG
, "Peer %s disconnected on socket %d\n",
196 ls
->str
, ls
->sockfd
);
200 ls
->bufpos
+= read_bytes
;
202 syslog (LOG_DEBUG
, "Has %d bytes from %s on socket %d\n",
203 ls
->bufpos
, ls
->str
, ls
->sockfd
);
208 #define C2I(buf) ((buf[3] & 0xFF) | \
209 ((buf[2] & 0xFF) << 8) | \
210 ((buf[1] & 0xFF) << 16) | \
211 ((buf[0] & 0xFF) << 24))
213 /* Have we read an entire request? */
215 kdc_ready (struct listenspec
*ls
)
217 int waitfor
= ls
->bufpos
>= 4 ? C2I (ls
->buf
) : 4;
219 syslog (LOG_DEBUG
, "Got %d bytes of %d bytes from %s on socket %d\n",
220 ls
->bufpos
, waitfor
+ 4, ls
->str
, ls
->sockfd
);
222 if (ls
->type
== SOCK_DGRAM
&& ls
->bufpos
> 0)
224 else if (ls
->bufpos
> 4 && waitfor
+ 4 == ls
->bufpos
)
230 /* Process a request and store reply in same buffer. */
232 kdc_process (struct listenspec
*ls
)
237 syslog (LOG_DEBUG
, "Processing %d from %s on socket %d",
238 ls
->bufpos
, ls
->str
, ls
->sockfd
);
240 if (ls
->type
== SOCK_DGRAM
)
241 plen
= process (ls
->buf
, ls
->bufpos
, &p
);
243 plen
= process (ls
->buf
+ 4, ls
->bufpos
- 4, &p
);
247 syslog (LOG_ERR
, "Processing request failed for %s on socket %d (%d)",
248 ls
->str
, ls
->sockfd
, plen
);
249 memcpy (ls
->buf
, fatal_krberror
, fatal_krberror_len
);
250 ls
->bufpos
= fatal_krberror_len
;
254 memcpy (ls
->buf
, p
, plen
);
259 syslog (LOG_DEBUG
, "Have %d bytes for %s on socket %d",
260 ls
->bufpos
, ls
->str
, ls
->sockfd
);
271 #define MAX(a,b) ((a) > (b) ? (a) : (b))
273 /* Main KDC logic, loop around select and call kdc_accept, kdc_read,
274 kdc_extension, kdc_process and kdc_send. This return when the
275 SIGINT or SIGTERM signals are received. */
279 struct listenspec
*ls
;
284 signal (SIGINT
, ctrlc
);
285 signal (SIGTERM
, ctrlc
);
288 syslog (LOG_DEBUG
, "Starting (GNUTLS `%s')", gnutls_check_version (NULL
));
290 syslog (LOG_DEBUG
, "Starting (no TLS)");
299 for (ls
= listenspec
; ls
; ls
= ls
->next
)
303 maxfd
= MAX (maxfd
, ls
->sockfd
+ 1);
305 syslog (LOG_DEBUG
, "Listening on %s socket %d\n",
306 ls
->str
, ls
->sockfd
);
307 FD_SET (ls
->sockfd
, &readfds
);
311 while ((rc
= select (maxfd
, &readfds
, NULL
, NULL
, NULL
)) == 0);
316 syslog (LOG_ERR
, "Error listening on sockets (%d): %s",
317 rc
, strerror (errno
));
321 for (ls
= listenspec
; ls
; ls
= ls
->next
)
322 if (ls
->sockfd
> 0 && FD_ISSET (ls
->sockfd
, &readfds
))
324 if (ls
->type
== SOCK_STREAM
&& ls
->listening
)
326 else if (kdc_read (ls
) < 0)
328 else if (kdc_extension (ls
) < 0)
330 else if (kdc_ready (ls
))
338 syslog (LOG_DEBUG
, "Shutting down");