Add.
[shishi.git] / src / server.c
blob26d397d64a36f3e20ca42cd08fe24cad29370ee4
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. */
26 #include "kdc.h"
28 /* Accept new TCP connection in a new listenspec entry. */
29 static void
30 kdc_accept (struct listenspec *ls)
32 struct listenspec *newls;
34 newls = xzalloc (sizeof (*newls));
35 newls->next = ls->next;
36 ls->next = newls;
38 newls->bufpos = 0;
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;
57 int rc;
59 syslog (LOG_INFO, "Closing %s socket %d", ls->str, ls->sockfd);
61 #ifdef USE_STARTTLS
62 if (ls->usetls)
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);
74 #endif
76 if (ls->sockfd)
78 rc = close (ls->sockfd);
79 if (rc != 0)
80 syslog (LOG_ERR, "Close failed to %s on socket %d (%d): %s",
81 ls->str, ls->sockfd, rc, strerror (rc));
84 if (ls->str)
85 free (ls->str);
87 for (tmp = listenspec; tmp && tmp->next != ls; tmp = tmp->next)
89 if (tmp)
90 tmp->next = ls->next;
92 free (ls);
94 return tmp;
97 /* Send string to peer, via UDP/TCP/TLS, reporting any errors. */
98 void
99 kdc_send1 (struct listenspec *ls)
101 ssize_t sent_bytes;
104 #ifdef USE_STARTTLS
105 if (ls->usetls)
106 sent_bytes = gnutls_record_send (ls->session, ls->buf, ls->bufpos);
107 else
108 #endif
109 sent_bytes = sendto (ls->sockfd, ls->buf, ls->bufpos,
110 0, &ls->addr, ls->addrlen);
111 while (sent_bytes == -1 && errno == EAGAIN);
113 if (sent_bytes < 0)
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
125 errors. */
126 static void
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);
132 else
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;
145 ls->bufpos += 4;
148 kdc_send1 (ls);
150 ls->bufpos = 0;
153 #ifndef USE_STARTTLS
154 /* Dummy function to replace starttls.c functionality. */
156 kdc_extension (struct listenspec *ls)
158 return 0;
160 #endif
162 /* Read data from peer, reporting any errors. */
163 static int
164 kdc_read (struct listenspec *ls)
166 ssize_t read_bytes;
168 ls->addrlen = sizeof (ls->addr);
169 #ifdef USE_STARTTLS
170 if (ls->usetls)
171 read_bytes = gnutls_record_recv (ls->session,
172 ls->buf + ls->bufpos,
173 sizeof (ls->buf) - ls->bufpos);
174 else
175 #endif
176 read_bytes = recvfrom (ls->sockfd, ls->buf + ls->bufpos,
177 sizeof (ls->buf) - ls->bufpos, 0,
178 &ls->addr, &ls->addrlen);
179 if (read_bytes < 0)
181 #ifdef USE_STARTTLS
182 if (ls->usetls)
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));
186 else
187 #endif
188 syslog (LOG_ERR, "Error reading from %s on socket %d (%d): %s",
189 ls->str, ls->sockfd, read_bytes, strerror (read_bytes));
190 return -1;
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);
197 return -1;
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);
205 return 0;
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? */
214 static int
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)
223 return 1;
224 else if (ls->bufpos > 4 && waitfor + 4 == ls->bufpos)
225 return 1;
227 return 0;
230 /* Process a request and store reply in same buffer. */
231 static void
232 kdc_process (struct listenspec *ls)
234 char *p;
235 ssize_t plen;
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);
242 else
243 plen = process (ls->buf + 4, ls->bufpos - 4, &p);
245 if (plen <= 0)
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;
252 else
254 memcpy (ls->buf, p, plen);
255 ls->bufpos = plen;
256 free (p);
259 syslog (LOG_DEBUG, "Have %d bytes for %s on socket %d",
260 ls->bufpos, ls->str, ls->sockfd);
263 int quit = 0;
265 static void
266 ctrlc (int signum)
268 quit = 1;
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. */
276 void
277 kdc_loop (void)
279 struct listenspec *ls;
280 fd_set readfds;
281 int maxfd = 0;
282 int rc;
284 signal (SIGINT, ctrlc);
285 signal (SIGTERM, ctrlc);
287 #ifdef USE_STARTTLS
288 syslog (LOG_DEBUG, "Starting (GNUTLS `%s')", gnutls_check_version (NULL));
289 #else
290 syslog (LOG_DEBUG, "Starting (no TLS)");
291 #endif
293 while (!quit)
297 FD_ZERO (&readfds);
298 maxfd = 0;
299 for (ls = listenspec; ls; ls = ls->next)
301 if (ls->sockfd > 0)
303 maxfd = MAX (maxfd, ls->sockfd + 1);
304 if (!arg.quiet_flag)
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);
313 if (rc < 0)
315 if (errno != EINTR)
316 syslog (LOG_ERR, "Error listening on sockets (%d): %s",
317 rc, strerror (errno));
318 continue;
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)
325 kdc_accept (ls);
326 else if (kdc_read (ls) < 0)
327 ls = kdc_close (ls);
328 else if (kdc_extension (ls) < 0)
329 ls = kdc_close (ls);
330 else if (kdc_ready (ls))
332 kdc_process (ls);
333 kdc_send (ls);
338 syslog (LOG_DEBUG, "Shutting down");