Branch data Line data Source code
1 : : /* netio.c --- Network I/O functions.
2 : : * Copyright (C) 2002, 2003, 2004, 2006, 2007, 2008, 2010 Simon Josefsson
3 : : *
4 : : * This file is part of Shishi.
5 : : *
6 : : * Shishi is free software; you can redistribute it and/or modify it
7 : : * 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.
10 : : *
11 : : * Shishi is distributed in the hope that it will be useful, but
12 : : * 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.
15 : : *
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
20 : : *
21 : : */
22 : :
23 : : #include "internal.h"
24 : :
25 : : /* Get _shishi_sendrecv_tls, etc. */
26 : : #include "starttls.h"
27 : :
28 : : /* Get _shishi_realminfo, etc. */
29 : : #include "diskio.h"
30 : :
31 : : /* Get _shishi_realminfo. */
32 : : #include "cfg.h"
33 : :
34 : : static int
35 : 0 : sendrecv_udp (Shishi * handle,
36 : : struct addrinfo *ai,
37 : : const char *indata, int inlen, char **outdata, size_t * outlen)
38 : : {
39 : : char tmpbuf[BUFSIZ]; /* XXX can we do without it?
40 : : MSG_PEEK|MSG_TRUNC doesn't work for udp.. */
41 : : int sockfd;
42 : : int bytes_sent;
43 : : fd_set readfds;
44 : : struct timeval tout;
45 : : ssize_t slen;
46 : : int rc;
47 : :
48 : 0 : sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
49 [ # # ]: 0 : if (sockfd < 0)
50 : : {
51 : 0 : shishi_error_set (handle, strerror (errno));
52 : 0 : return SHISHI_SOCKET_ERROR;
53 : : }
54 : :
55 [ # # ]: 0 : if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) != 0)
56 : : {
57 : 0 : shishi_error_set (handle, strerror (errno));
58 : 0 : close (sockfd);
59 : 0 : return SHISHI_BIND_ERROR;
60 : : }
61 : :
62 : 0 : bytes_sent = write (sockfd, indata, inlen);
63 [ # # ]: 0 : if (bytes_sent != inlen)
64 : : {
65 : 0 : shishi_error_set (handle, strerror (errno));
66 : 0 : close (sockfd);
67 : 0 : return SHISHI_SENDTO_ERROR;
68 : : }
69 : :
70 : 0 : FD_ZERO (&readfds);
71 : 0 : FD_SET (sockfd, &readfds);
72 : 0 : tout.tv_sec = handle->kdctimeout;
73 : 0 : tout.tv_usec = 0;
74 [ # # ]: 0 : if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
75 : : {
76 [ # # ]: 0 : if (rc == -1)
77 : 0 : shishi_error_set (handle, strerror (errno));
78 : : else
79 : 0 : shishi_error_clear (handle);
80 : 0 : close (sockfd);
81 : 0 : return SHISHI_KDC_TIMEOUT;
82 : : }
83 : :
84 : 0 : *outlen = sizeof (tmpbuf);
85 : 0 : slen = read (sockfd, tmpbuf, *outlen);
86 [ # # ]: 0 : if (slen == -1)
87 : : {
88 : 0 : shishi_error_set (handle, strerror (errno));
89 : 0 : close (sockfd);
90 : 0 : return SHISHI_RECVFROM_ERROR;
91 : : }
92 : :
93 : 0 : *outdata = xmalloc (slen);
94 : 0 : *outlen = slen;
95 : 0 : memcpy (*outdata, tmpbuf, slen);
96 : :
97 [ # # ]: 0 : if (close (sockfd) != 0)
98 : : {
99 : 0 : shishi_error_set (handle, strerror (errno));
100 : 0 : return SHISHI_CLOSE_ERROR;
101 : : }
102 : :
103 : 0 : return SHISHI_OK;
104 : : }
105 : :
106 : : static int
107 : 0 : sendrecv_tcp (Shishi * handle,
108 : : struct addrinfo *ai,
109 : : const char *indata, int inlen, char **outdata, size_t * outlen)
110 : : {
111 : : char tmpbuf[BUFSIZ]; /* XXX can we do without it?
112 : : MSG_PEEK|MSG_TRUNC doesn't work for udp.. */
113 : : int sockfd;
114 : : int bytes_sent;
115 : : struct sockaddr_storage from_sa;
116 : 0 : socklen_t length = sizeof (struct sockaddr_storage);
117 : : fd_set readfds;
118 : : struct timeval tout;
119 : : int rc;
120 : : ssize_t slen;
121 : :
122 : 0 : sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
123 [ # # ]: 0 : if (sockfd < 0)
124 : : {
125 : 0 : shishi_error_set (handle, strerror (errno));
126 : 0 : return SHISHI_SOCKET_ERROR;
127 : : }
128 : :
129 [ # # ]: 0 : if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) != 0)
130 : : {
131 : 0 : shishi_error_set (handle, strerror (errno));
132 : 0 : close (sockfd);
133 : 0 : return SHISHI_BIND_ERROR;
134 : : }
135 : :
136 : 0 : tmpbuf[3] = inlen & 0xFF;
137 : 0 : tmpbuf[2] = (inlen >> 8) & 0xFF;
138 : 0 : tmpbuf[1] = (inlen >> 16) & 0xFF;
139 : 0 : tmpbuf[0] = (inlen >> 24) & 0xFF;
140 : :
141 : 0 : bytes_sent = write (sockfd, tmpbuf, 4);
142 [ # # ]: 0 : if (bytes_sent != 4)
143 : : {
144 : 0 : shishi_error_set (handle, strerror (errno));
145 : 0 : return SHISHI_SENDTO_ERROR;
146 : : }
147 : :
148 : 0 : bytes_sent = write (sockfd, (const void *) indata, inlen);
149 [ # # ]: 0 : if (bytes_sent != inlen)
150 : : {
151 : 0 : shishi_error_set (handle, strerror (errno));
152 : 0 : return SHISHI_SENDTO_ERROR;
153 : : }
154 : :
155 : 0 : FD_ZERO (&readfds);
156 : 0 : FD_SET (sockfd, &readfds);
157 : 0 : tout.tv_sec = handle->kdctimeout;
158 : 0 : tout.tv_usec = 0;
159 [ # # ]: 0 : if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
160 : : {
161 [ # # ]: 0 : if (rc == -1)
162 : 0 : shishi_error_set (handle, strerror (errno));
163 : : else
164 : 0 : shishi_error_clear (handle);
165 : 0 : return SHISHI_KDC_TIMEOUT;
166 : : }
167 : :
168 : 0 : *outlen = 4;
169 : 0 : slen = recvfrom (sockfd, tmpbuf, *outlen, 0,
170 : : (struct sockaddr *) &from_sa, &length);
171 [ # # ]: 0 : if (slen == -1)
172 : : {
173 : 0 : shishi_error_set (handle, strerror (errno));
174 : 0 : return SHISHI_RECVFROM_ERROR;
175 : : }
176 : :
177 : 0 : *outlen = sizeof (tmpbuf);
178 : 0 : slen = recvfrom (sockfd, tmpbuf, *outlen, 0,
179 : : (struct sockaddr *) &from_sa, &length);
180 [ # # ]: 0 : if (slen == -1)
181 : : {
182 : 0 : shishi_error_set (handle, strerror (errno));
183 : 0 : return SHISHI_RECVFROM_ERROR;
184 : : }
185 : :
186 : 0 : *outdata = xmalloc (slen);
187 : 0 : *outlen = slen;
188 : 0 : memcpy (*outdata, tmpbuf, slen);
189 : :
190 [ # # ]: 0 : if (close (sockfd) != 0)
191 : : {
192 : 0 : shishi_error_set (handle, strerror (errno));
193 : 0 : return SHISHI_CLOSE_ERROR;
194 : : }
195 : :
196 : 0 : return SHISHI_OK;
197 : : }
198 : :
199 : : static int
200 : 0 : sendrecv_host (Shishi * handle,
201 : : int transport, const char *host, const char *port,
202 : : const char *indata, size_t inlen,
203 : : char **outdata, size_t * outlen)
204 : : {
205 : : struct addrinfo hints;
206 : : struct addrinfo *ai;
207 : : int rc;
208 : :
209 : 0 : memset (&hints, 0, sizeof (hints));
210 [ # # ][ # # ]: 0 : if (transport == TCP || transport == TLS)
211 : 0 : hints.ai_socktype = SOCK_STREAM;
212 : : else
213 : 0 : hints.ai_socktype = SOCK_DGRAM;
214 : 0 : hints.ai_flags = AI_ADDRCONFIG;
215 : :
216 [ # # ]: 0 : if (port == NULL)
217 : 0 : port = "88";
218 : :
219 : 0 : rc = getaddrinfo (host, port, &hints, &ai);
220 [ # # ]: 0 : if (rc != 0)
221 : : {
222 : 0 : shishi_error_printf (handle, "Cannot find host %s", host);
223 : 0 : return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
224 : : }
225 : :
226 : : do
227 : : {
228 : : char nodename[NI_MAXHOST];
229 : 0 : size_t j = 0;
230 : :
231 : 0 : rc = getnameinfo (ai->ai_addr, ai->ai_addrlen,
232 : : nodename, sizeof (nodename), NULL, 0, NI_NUMERICHOST);
233 [ # # ]: 0 : shishi_verbose (handle, "Sending to %s (%s) port %s transport %s",
234 : : host, rc == 0 ? nodename : "unknown address", port,
235 : : _shishi_transport2string (transport));
236 : :
237 : : do
238 : : {
239 [ # # ]: 0 : if (transport == TCP)
240 : 0 : rc = sendrecv_tcp (handle, ai, indata, inlen, outdata, outlen);
241 : : #ifdef USE_STARTTLS
242 [ # # ]: 0 : else if (transport == TLS)
243 : 0 : rc = _shishi_sendrecv_tls (handle, ai, indata, inlen,
244 : : outdata, outlen);
245 : : #endif
246 : : else
247 : 0 : rc = sendrecv_udp (handle, ai, indata, inlen, outdata, outlen);
248 : :
249 [ # # ]: 0 : if (rc != SHISHI_OK)
250 : 0 : shishi_verbose (handle, "Error sending to KDC: %s",
251 : : shishi_strerror (rc));
252 : : }
253 [ # # ][ # # ]: 0 : while (rc == SHISHI_KDC_TIMEOUT && ++j < handle->kdcretries);
254 : : }
255 [ # # ][ # # ]: 0 : while (rc != SHISHI_OK && (ai = ai->ai_next));
256 : :
257 : 0 : return rc;
258 : : }
259 : :
260 : : static int
261 : 0 : sendrecv_srv3 (Shishi * handle,
262 : : int transport,
263 : : const char *realm,
264 : : const char *indata, size_t inlen,
265 : : char **outdata, size_t * outlen,
266 : : Shishi_dns rrs, bool * found_srv_records)
267 : : {
268 : 0 : int rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
269 : :
270 [ # # ]: 0 : for (; rrs; rrs = rrs->next)
271 : : {
272 : 0 : Shishi_dns_srv srv = rrs->rr;
273 : : char *port;
274 : :
275 [ # # ]: 0 : if (rrs->class != SHISHI_DNS_IN)
276 : 0 : continue;
277 [ # # ]: 0 : if (rrs->type != SHISHI_DNS_SRV)
278 : 0 : continue;
279 : :
280 : 0 : shishi_verbose (handle, "Found SRV host %s port %d",
281 : 0 : srv->name, srv->port);
282 : 0 : *found_srv_records = true;
283 : :
284 : 0 : port = xasprintf ("%d", srv->port);
285 : 0 : rc = sendrecv_host (handle, transport,
286 : : srv->name, port, indata, inlen, outdata, outlen);
287 : 0 : free (port);
288 : :
289 [ # # ]: 0 : if (rc == SHISHI_OK)
290 : 0 : return rc;
291 : : }
292 : :
293 : 0 : return rc;
294 : : }
295 : :
296 : : static int
297 : 0 : sendrecv_srv2 (Shishi * handle,
298 : : int transport,
299 : : const char *realm,
300 : : const char *indata, size_t inlen,
301 : : char **outdata, size_t * outlen, bool * found_srv_records)
302 : : {
303 : : Shishi_dns rrs;
304 : : char *tmp;
305 : : int rc;
306 : :
307 [ # # ][ # # ]: 0 : if (transport != UDP && transport != TCP)
308 : 0 : return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
309 : :
310 [ # # ]: 0 : tmp = xasprintf ("_kerberos._%s.%s", transport == UDP ? "udp" : "tcp",
311 : : realm);
312 : 0 : shishi_verbose (handle, "Looking up SRV for %s", tmp);
313 : 0 : rrs = shishi_resolv (tmp, SHISHI_DNS_SRV);
314 : 0 : free (tmp);
315 : :
316 [ # # ]: 0 : if (rrs)
317 : 0 : rc = sendrecv_srv3 (handle, transport, realm, indata, inlen,
318 : : outdata, outlen, rrs, found_srv_records);
319 : : else
320 : 0 : rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
321 : :
322 : 0 : shishi_resolv_free (rrs);
323 : :
324 : 0 : return rc;
325 : : }
326 : :
327 : : static int
328 : 0 : sendrecv_srv (Shishi * handle, const char *realm,
329 : : const char *indata, size_t inlen,
330 : : char **outdata, size_t * outlen, bool * found_srv_records)
331 : : {
332 : 0 : int rc = sendrecv_srv2 (handle, UDP, realm, indata, inlen,
333 : 0 : outdata, outlen, found_srv_records);
334 [ # # ]: 0 : if (rc == SHISHI_OK)
335 : 0 : return rc;
336 : 0 : return sendrecv_srv2 (handle, TCP, realm, indata, inlen,
337 : : outdata, outlen, found_srv_records);
338 : : }
339 : :
340 : : static int
341 : 0 : sendrecv_static (Shishi * handle, const char *realm,
342 : : const char *indata, size_t inlen,
343 : : char **outdata, size_t * outlen)
344 : : {
345 : : struct Shishi_realminfo *ri;
346 : : size_t k;
347 : : int rc;
348 : :
349 : 0 : ri = _shishi_realminfo (handle, realm);
350 [ # # # # ]: 0 : if (!ri || ri->nkdcaddresses == 0)
351 : : {
352 : 0 : shishi_error_printf (handle, "No KDC configured for %s", realm);
353 : 0 : return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
354 : : }
355 : :
356 : 0 : rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
357 [ # # ]: 0 : for (k = 0; k < ri->nkdcaddresses; k++)
358 : : {
359 : 0 : rc = sendrecv_host (handle,
360 : 0 : ri->kdcaddresses[k].transport,
361 : 0 : ri->kdcaddresses[k].hostname,
362 : 0 : ri->kdcaddresses[k].port,
363 : : indata, inlen, outdata, outlen);
364 [ # # ]: 0 : if (rc == SHISHI_OK)
365 : 0 : return rc;
366 : : }
367 : :
368 : 0 : return rc;
369 : : }
370 : :
371 : : /**
372 : : * shishi_kdc_sendrecv_hint:
373 : : * @handle: Shishi library handle create by shishi_init().
374 : : * @realm: string with realm name.
375 : : * @indata: Packet to send to KDC.
376 : : * @inlen: Length of @indata.
377 : : * @outdata: Newly allocated string with data returned from KDC.
378 : : * @outlen: Length of @outdata.
379 : : * @hint: a #Shishi_tkts_hint structure with flags.
380 : : *
381 : : * Send packet to KDC for realm and receive response. The code finds
382 : : * KDC addresses from configuration file, then by querying for SRV
383 : : * records for the realm, and finally by using the realm name as a
384 : : * hostname.
385 : : *
386 : : * Returns: %SHISHI_OK on success, %SHISHI_KDC_TIMEOUT if a timeout
387 : : * was reached, or other errors.
388 : : **/
389 : : int
390 : 0 : shishi_kdc_sendrecv_hint (Shishi * handle, const char *realm,
391 : : const char *indata, size_t inlen,
392 : : char **outdata, size_t * outlen,
393 : : Shishi_tkts_hint * hint)
394 : : {
395 : : struct Shishi_realminfo *ri;
396 : 0 : bool found_srv_records = false;
397 : : int rc;
398 : :
399 : 0 : ri = _shishi_realminfo (handle, realm);
400 [ # # # # ]: 0 : if (ri && ri->nkdcaddresses > 0)
401 : : /* If we have configured KDCs, never use DNS or direct method. */
402 : 0 : return sendrecv_static (handle, realm, indata, inlen, outdata, outlen);
403 : :
404 : 0 : rc = sendrecv_srv (handle, realm, indata, inlen, outdata, outlen,
405 : : &found_srv_records);
406 [ # # # # ]: 0 : if (rc != SHISHI_OK && !found_srv_records)
407 : : {
408 : 0 : shishi_verbose (handle, "No SRV RRs, trying realm host mapping for %s",
409 : : realm);
410 : 0 : rc = sendrecv_host (handle, UDP, realm, NULL,
411 : : indata, inlen, outdata, outlen);
412 : : }
413 : :
414 : 0 : return rc;
415 : : }
416 : :
417 : : /**
418 : : * shishi_kdc_sendrecv:
419 : : * @handle: Shishi library handle create by shishi_init().
420 : : * @realm: string with realm name.
421 : : * @indata: Packet to send to KDC.
422 : : * @inlen: Length of @indata.
423 : : * @outdata: Newly allocated string with data returned from KDC.
424 : : * @outlen: Length of @outdata.
425 : : *
426 : : * Send packet to KDC for realm and receive response. The code finds
427 : : * KDC addresses from configuration file, then by querying for SRV
428 : : * records for the realm, and finally by using the realm name as a
429 : : * hostname.
430 : : *
431 : : * Returns: %SHISHI_OK on success, %SHISHI_KDC_TIMEOUT if a timeout
432 : : * was reached, or other errors.
433 : : **/
434 : : int
435 : 0 : shishi_kdc_sendrecv (Shishi * handle, const char *realm,
436 : : const char *indata, size_t inlen,
437 : : char **outdata, size_t * outlen)
438 : : {
439 : 0 : return shishi_kdc_sendrecv_hint (handle, realm, indata, inlen,
440 : : outdata, outlen, NULL);
441 : : }
|