Add.
[shishi.git] / src / server.c
blob1bdc6392b1e945dff04de5a587516c2729b19afc
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. */
25 #include "kdc.h"
27 /* Accept new TCP connection in a new listenspec entry. */
28 static void
29 kdc_accept (struct listenspec *ls)
31 struct listenspec *newls;
33 newls = xzalloc (sizeof (*newls));
34 newls->next = ls->next;
35 ls->next = newls;
37 newls->bufpos = 0;
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;
56 int rc;
58 syslog (LOG_INFO, "Closing %s socket %d", ls->str, ls->sockfd);
60 #ifdef USE_STARTTLS
61 if (ls->usetls)
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);
73 #endif
75 if (ls->sockfd)
77 rc = close (ls->sockfd);
78 if (rc != 0)
79 syslog (LOG_ERR, "Close failed to %s on socket %d (%d): %s",
80 ls->str, ls->sockfd, rc, strerror (rc));
83 if (ls->str)
84 free (ls->str);
86 for (tmp = listenspec; tmp && tmp->next != ls; tmp = tmp->next)
88 if (tmp)
89 tmp->next = ls->next;
91 free (ls);
93 return tmp;
96 /* Send string to peer, via UDP/TCP/TLS, reporting any errors. */
97 void
98 kdc_send1 (struct listenspec *ls)
100 ssize_t sent_bytes;
103 #ifdef USE_STARTTLS
104 if (ls->usetls)
105 sent_bytes = gnutls_record_send (ls->session, ls->buf, ls->bufpos);
106 else
107 #endif
108 sent_bytes = sendto (ls->sockfd, ls->buf, ls->bufpos,
109 0, &ls->addr, ls->addrlen);
110 while (sent_bytes == -1 && errno == EAGAIN);
112 if (sent_bytes < 0)
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
124 errors. */
125 static void
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);
131 else
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;
144 ls->bufpos += 4;
147 kdc_send1 (ls);
149 ls->bufpos = 0;
152 #ifndef USE_STARTTLS
153 /* Dummy function to replace starttls.c functionality. */
155 kdc_extension (struct listenspec *ls)
157 return 0;
159 #endif
161 /* Read data from peer, reporting any errors. */
162 static int
163 kdc_read (struct listenspec *ls)
165 ssize_t read_bytes;
167 ls->addrlen = sizeof (ls->addr);
168 #ifdef USE_STARTTLS
169 if (ls->usetls)
170 read_bytes = gnutls_record_recv (ls->session,
171 ls->buf + ls->bufpos,
172 sizeof (ls->buf) - ls->bufpos);
173 else
174 #endif
175 read_bytes = recvfrom (ls->sockfd, ls->buf + ls->bufpos,
176 sizeof (ls->buf) - ls->bufpos, 0,
177 &ls->addr, &ls->addrlen);
178 if (read_bytes < 0)
180 #ifdef USE_STARTTLS
181 if (ls->usetls)
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));
185 else
186 #endif
187 syslog (LOG_ERR, "Error reading from %s on socket %d (%d): %s",
188 ls->str, ls->sockfd, read_bytes, strerror (read_bytes));
189 return -1;
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);
196 return -1;
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);
204 return 0;
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? */
213 static int
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)
222 return 1;
223 else if (ls->bufpos > 4 && waitfor + 4 == ls->bufpos)
224 return 1;
226 return 0;
229 /* Process a request and store reply in same buffer. */
230 static void
231 kdc_process (struct listenspec *ls)
233 char *p;
234 ssize_t plen;
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);
241 else
242 plen = process (ls->buf + 4, ls->bufpos - 4, &p);
244 if (plen <= 0)
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;
251 else
253 memcpy (ls->buf, p, plen);
254 ls->bufpos = plen;
255 free (p);
258 syslog (LOG_DEBUG, "Have %d bytes for %s on socket %d",
259 ls->bufpos, ls->str, ls->sockfd);
262 int quit = 0;
264 static void
265 ctrlc (int signum)
267 quit = 1;
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. */
275 void
276 kdc_loop (void)
278 struct listenspec *ls;
279 fd_set readfds;
280 int maxfd = 0;
281 int rc;
283 signal (SIGINT, ctrlc);
284 signal (SIGTERM, ctrlc);
286 #ifdef USE_STARTTLS
287 syslog (LOG_DEBUG, "Starting (GNUTLS `%s')", gnutls_check_version (NULL));
288 #else
289 syslog (LOG_DEBUG, "Starting (no TLS)");
290 #endif
292 while (!quit)
296 FD_ZERO (&readfds);
297 maxfd = 0;
298 for (ls = listenspec; ls; ls = ls->next)
300 if (ls->sockfd > 0)
302 maxfd = MAX (maxfd, ls->sockfd + 1);
303 if (!arg.quiet_flag)
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);
312 if (rc < 0)
314 if (errno != EINTR)
315 syslog (LOG_ERR, "Error listening on sockets (%d): %s",
316 rc, strerror (errno));
317 continue;
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)
324 kdc_accept (ls);
325 else if (kdc_read (ls) < 0)
326 ls = kdc_close (ls);
327 else if (kdc_extension (ls) < 0)
328 ls = kdc_close (ls);
329 else if (kdc_ready (ls))
331 kdc_process (ls);
332 kdc_send (ls);
337 syslog (LOG_DEBUG, "Shutting down");