1 /* netio.c --- Network I/O functions.
2 * Copyright (C) 2002-2013 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
sendrecv_udp(Shishi * handle,struct addrinfo * ai,const char * indata,int inlen,char ** outdata,size_t * outlen)35 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 sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
49 if (sockfd < 0)
50 {
51 shishi_error_set (handle, strerror (errno));
52 return SHISHI_SOCKET_ERROR;
53 }
54
55 if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) != 0)
56 {
57 shishi_error_set (handle, strerror (errno));
58 close (sockfd);
59 return SHISHI_BIND_ERROR;
60 }
61
62 bytes_sent = write (sockfd, indata, inlen);
63 if (bytes_sent != inlen)
64 {
65 shishi_error_set (handle, strerror (errno));
66 close (sockfd);
67 return SHISHI_SENDTO_ERROR;
68 }
69
70 FD_ZERO (&readfds);
71 FD_SET (sockfd, &readfds);
72 tout.tv_sec = handle->kdctimeout;
73 tout.tv_usec = 0;
74 if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
75 {
76 if (rc == -1)
77 shishi_error_set (handle, strerror (errno));
78 else
79 shishi_error_clear (handle);
80 close (sockfd);
81 return SHISHI_KDC_TIMEOUT;
82 }
83
84 *outlen = sizeof (tmpbuf);
85 slen = read (sockfd, tmpbuf, *outlen);
86 if (slen == -1)
87 {
88 shishi_error_set (handle, strerror (errno));
89 close (sockfd);
90 return SHISHI_RECVFROM_ERROR;
91 }
92
93 *outdata = xmalloc (slen);
94 *outlen = slen;
95 memcpy (*outdata, tmpbuf, slen);
96
97 if (close (sockfd) != 0)
98 {
99 shishi_error_set (handle, strerror (errno));
100 return SHISHI_CLOSE_ERROR;
101 }
102
103 return SHISHI_OK;
104 }
105
106 static int
sendrecv_tcp(Shishi * handle,struct addrinfo * ai,const char * indata,int inlen,char ** outdata,size_t * outlen)107 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 socklen_t length = sizeof (struct sockaddr_storage);
117 fd_set readfds;
118 struct timeval tout;
119 int rc;
120 ssize_t slen;
121
122 sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
123 if (sockfd < 0)
124 {
125 shishi_error_set (handle, strerror (errno));
126 return SHISHI_SOCKET_ERROR;
127 }
128
129 if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) != 0)
130 {
131 shishi_error_set (handle, strerror (errno));
132 close (sockfd);
133 return SHISHI_BIND_ERROR;
134 }
135
136 tmpbuf[3] = inlen & 0xFF;
137 tmpbuf[2] = (inlen >> 8) & 0xFF;
138 tmpbuf[1] = (inlen >> 16) & 0xFF;
139 tmpbuf[0] = (inlen >> 24) & 0xFF;
140
141 bytes_sent = write (sockfd, tmpbuf, 4);
142 if (bytes_sent != 4)
143 {
144 shishi_error_set (handle, strerror (errno));
145 return SHISHI_SENDTO_ERROR;
146 }
147
148 bytes_sent = write (sockfd, (const void *) indata, inlen);
149 if (bytes_sent != inlen)
150 {
151 shishi_error_set (handle, strerror (errno));
152 return SHISHI_SENDTO_ERROR;
153 }
154
155 FD_ZERO (&readfds);
156 FD_SET (sockfd, &readfds);
157 tout.tv_sec = handle->kdctimeout;
158 tout.tv_usec = 0;
159 if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
160 {
161 if (rc == -1)
162 shishi_error_set (handle, strerror (errno));
163 else
164 shishi_error_clear (handle);
165 return SHISHI_KDC_TIMEOUT;
166 }
167
168 *outlen = 4;
169 slen = recvfrom (sockfd, tmpbuf, *outlen, 0,
170 (struct sockaddr *) &from_sa, &length);
171 if (slen == -1)
172 {
173 shishi_error_set (handle, strerror (errno));
174 return SHISHI_RECVFROM_ERROR;
175 }
176
177 *outlen = sizeof (tmpbuf);
178 slen = recvfrom (sockfd, tmpbuf, *outlen, 0,
179 (struct sockaddr *) &from_sa, &length);
180 if (slen == -1)
181 {
182 shishi_error_set (handle, strerror (errno));
183 return SHISHI_RECVFROM_ERROR;
184 }
185
186 *outdata = xmalloc (slen);
187 *outlen = slen;
188 memcpy (*outdata, tmpbuf, slen);
189
190 if (close (sockfd) != 0)
191 {
192 shishi_error_set (handle, strerror (errno));
193 return SHISHI_CLOSE_ERROR;
194 }
195
196 return SHISHI_OK;
197 }
198
199 static int
sendrecv_host(Shishi * handle,int transport,const char * host,const char * port,const char * indata,size_t inlen,char ** outdata,size_t * outlen)200 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 memset (&hints, 0, sizeof (hints));
210 if (transport == TCP || transport == TLS)
211 hints.ai_socktype = SOCK_STREAM;
212 else
213 hints.ai_socktype = SOCK_DGRAM;
214 hints.ai_flags = AI_ADDRCONFIG;
215
216 if (port == NULL)
217 port = "88";
218
219 rc = getaddrinfo (host, port, &hints, &ai);
220 if (rc != 0)
221 {
222 shishi_error_printf (handle, "Cannot find host %s", host);
223 return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
224 }
225
226 do
227 {
228 char nodename[NI_MAXHOST];
229 size_t j = 0;
230
231 rc = getnameinfo (ai->ai_addr, ai->ai_addrlen,
232 nodename, sizeof (nodename), NULL, 0, NI_NUMERICHOST);
233 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 if (transport == TCP)
240 rc = sendrecv_tcp (handle, ai, indata, inlen, outdata, outlen);
241 #ifdef USE_STARTTLS
242 else if (transport == TLS)
243 rc = _shishi_sendrecv_tls (handle, ai, indata, inlen,
244 outdata, outlen);
245 #endif
246 else
247 rc = sendrecv_udp (handle, ai, indata, inlen, outdata, outlen);
248
249 if (rc != SHISHI_OK)
250 shishi_verbose (handle, "Error sending to KDC: %s",
251 shishi_strerror (rc));
252 }
253 while (rc == SHISHI_KDC_TIMEOUT && ++j < handle->kdcretries);
254 }
255 while (rc != SHISHI_OK && (ai = ai->ai_next));
256
257 return rc;
258 }
259
260 static int
sendrecv_srv3(Shishi * handle,int transport,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen,Shishi_dns rrs,bool * found_srv_records)261 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 int rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
269
270 for (; rrs; rrs = rrs->next)
271 {
272 Shishi_dns_srv srv = rrs->rr;
273 char *port;
274
275 if (rrs->class != SHISHI_DNS_IN)
276 continue;
277 if (rrs->type != SHISHI_DNS_SRV)
278 continue;
279
280 shishi_verbose (handle, "Found SRV host %s port %d",
281 srv->name, srv->port);
282 *found_srv_records = true;
283
284 port = xasprintf ("%d", srv->port);
285 rc = sendrecv_host (handle, transport,
286 srv->name, port, indata, inlen, outdata, outlen);
287 free (port);
288
289 if (rc == SHISHI_OK)
290 return rc;
291 }
292
293 return rc;
294 }
295
296 static int
sendrecv_srv2(Shishi * handle,int transport,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen,bool * found_srv_records)297 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 if (transport != UDP && transport != TCP)
308 return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
309
310 tmp = xasprintf ("_kerberos._%s.%s", transport == UDP ? "udp" : "tcp",
311 realm);
312 shishi_verbose (handle, "Looking up SRV for %s", tmp);
313 rrs = shishi_resolv (tmp, SHISHI_DNS_SRV);
314 free (tmp);
315
316 if (rrs)
317 rc = sendrecv_srv3 (handle, transport, realm, indata, inlen,
318 outdata, outlen, rrs, found_srv_records);
319 else
320 rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
321
322 shishi_resolv_free (rrs);
323
324 return rc;
325 }
326
327 static int
sendrecv_srv(Shishi * handle,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen,bool * found_srv_records)328 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 int rc = sendrecv_srv2 (handle, UDP, realm, indata, inlen,
333 outdata, outlen, found_srv_records);
334 if (rc == SHISHI_OK)
335 return rc;
336 return sendrecv_srv2 (handle, TCP, realm, indata, inlen,
337 outdata, outlen, found_srv_records);
338 }
339
340 static int
sendrecv_static(Shishi * handle,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen)341 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 ri = _shishi_realminfo (handle, realm);
350 if (!ri || ri->nkdcaddresses == 0)
351 {
352 shishi_error_printf (handle, "No KDC configured for %s", realm);
353 return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
354 }
355
356 rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
357 for (k = 0; k < ri->nkdcaddresses; k++)
358 {
359 rc = sendrecv_host (handle,
360 ri->kdcaddresses[k].transport,
361 ri->kdcaddresses[k].hostname,
362 ri->kdcaddresses[k].port,
363 indata, inlen, outdata, outlen);
364 if (rc == SHISHI_OK)
365 return rc;
366 }
367
368 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
shishi_kdc_sendrecv_hint(Shishi * handle,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen,Shishi_tkts_hint * hint)390 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 bool found_srv_records = false;
397 int rc;
398
399 ri = _shishi_realminfo (handle, realm);
400 if (ri && ri->nkdcaddresses > 0)
401 /* If we have configured KDCs, never use DNS or direct method. */
402 return sendrecv_static (handle, realm, indata, inlen, outdata, outlen);
403
404 rc = sendrecv_srv (handle, realm, indata, inlen, outdata, outlen,
405 &found_srv_records);
406 if (rc != SHISHI_OK && !found_srv_records)
407 {
408 shishi_verbose (handle, "No SRV RRs, trying realm host mapping for %s",
409 realm);
410 rc = sendrecv_host (handle, UDP, realm, NULL,
411 indata, inlen, outdata, outlen);
412 }
413
414 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
shishi_kdc_sendrecv(Shishi * handle,const char * realm,const char * indata,size_t inlen,char ** outdata,size_t * outlen)435 shishi_kdc_sendrecv (Shishi * handle, const char *realm,
436 const char *indata, size_t inlen,
437 char **outdata, size_t * outlen)
438 {
439 return shishi_kdc_sendrecv_hint (handle, realm, indata, inlen,
440 outdata, outlen, NULL);
441 }
442