No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / kcm / connect.c
blobbc041abd52e148f49ff392d73cefb6c59ee59f87
1 /*
2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kcm_locl.h"
36 __RCSID("$Heimdal: connect.c 16314 2005-11-29 19:03:50Z lha $"
37 "$NetBSD$");
39 struct descr {
40 int s;
41 int type;
42 char *path;
43 unsigned char *buf;
44 size_t size;
45 size_t len;
46 time_t timeout;
47 struct sockaddr_storage __ss;
48 struct sockaddr *sa;
49 socklen_t sock_len;
50 kcm_client peercred;
53 static void
54 init_descr(struct descr *d)
56 memset(d, 0, sizeof(*d));
57 d->sa = (struct sockaddr *)&d->__ss;
58 d->s = -1;
62 * re-initialize all `n' ->sa in `d'.
65 static void
66 reinit_descrs (struct descr *d, int n)
68 int i;
70 for (i = 0; i < n; ++i)
71 d[i].sa = (struct sockaddr *)&d[i].__ss;
75 * Update peer credentials from socket.
77 * SCM_CREDS can only be updated the first time there is read data to
78 * read from the filedescriptor, so if we read do it before this
79 * point, the cred data might not be is not there yet.
82 static int
83 update_client_creds(int s, kcm_client *peer)
85 #ifdef GETPEERUCRED
86 /* Solaris 10 */
88 ucred_t *peercred;
90 if (getpeerucred(s, &peercred) != 0) {
91 peer->uid = ucred_geteuid(peercred);
92 peer->gid = ucred_getegid(peercred);
93 peer->pid = 0;
94 ucred_free(peercred);
95 return 0;
98 #endif
99 #ifdef GETPEEREID
100 /* FreeBSD, OpenBSD */
102 uid_t uid;
103 gid_t gid;
105 if (getpeereid(s, &uid, &gid) == 0) {
106 peer->uid = uid;
107 peer->gid = gid;
108 peer->pid = 0;
109 return 0;
112 #endif
113 #ifdef SO_PEERCRED
114 /* Linux */
116 struct ucred pc;
117 socklen_t pclen = sizeof(pc);
119 if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
120 peer->uid = pc.uid;
121 peer->gid = pc.gid;
122 peer->pid = pc.pid;
123 return 0;
126 #endif
127 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
129 struct xucred peercred;
130 socklen_t peercredlen = sizeof(peercred);
132 if (getsockopt(s, LOCAL_PEERCRED, 1,
133 (void *)&peercred, &peercredlen) == 0
134 && peercred.cr_version == XUCRED_VERSION)
136 peer->uid = peercred.cr_uid;
137 peer->gid = peercred.cr_gid;
138 peer->pid = 0;
139 return 0;
142 #endif
143 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
144 /* NetBSD */
145 if (peer->uid == -1) {
146 struct msghdr msg;
147 socklen_t crmsgsize;
148 void *crmsg;
149 struct cmsghdr *cmp;
150 struct sockcred *sc;
152 memset(&msg, 0, sizeof(msg));
153 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
154 if (crmsgsize == 0)
155 return 1 ;
157 crmsg = malloc(crmsgsize);
158 if (crmsg == NULL)
159 goto failed_scm_creds;
161 memset(crmsg, 0, crmsgsize);
163 msg.msg_control = crmsg;
164 msg.msg_controllen = crmsgsize;
166 if (recvmsg(s, &msg, 0) < 0) {
167 free(crmsg);
168 goto failed_scm_creds;
171 if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
172 free(crmsg);
173 goto failed_scm_creds;
176 cmp = CMSG_FIRSTHDR(&msg);
177 if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
178 free(crmsg);
179 goto failed_scm_creds;
182 sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
184 peer->uid = sc->sc_euid;
185 peer->gid = sc->sc_egid;
186 peer->pid = 0;
188 free(crmsg);
189 return 0;
190 } else {
191 /* we already got the cred, just return it */
192 return 0;
194 failed_scm_creds:
195 #endif
196 krb5_warn(kcm_context, errno, "failed to determine peer identity");
197 return 1;
202 * Create the socket (family, type, port) in `d'
205 static void
206 init_socket(struct descr *d)
208 struct sockaddr_un un;
209 struct sockaddr *sa = (struct sockaddr *)&un;
210 krb5_socklen_t sa_size = sizeof(un);
212 init_descr (d);
214 un.sun_family = AF_UNIX;
216 if (socket_path != NULL)
217 d->path = socket_path;
218 else
219 d->path = _PATH_KCM_SOCKET;
221 strlcpy(un.sun_path, d->path, sizeof(un.sun_path));
223 d->s = socket(AF_UNIX, SOCK_STREAM, 0);
224 if (d->s < 0){
225 krb5_warn(kcm_context, errno, "socket(%d, %d, 0)", AF_UNIX, SOCK_STREAM);
226 d->s = -1;
227 return;
229 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
231 int one = 1;
232 setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
234 #endif
235 #ifdef LOCAL_CREDS
237 int one = 1;
238 setsockopt(d->s, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
240 #endif
242 d->type = SOCK_STREAM;
244 unlink(d->path);
246 if (bind(d->s, sa, sa_size) < 0) {
247 krb5_warn(kcm_context, errno, "bind %s", un.sun_path);
248 close(d->s);
249 d->s = -1;
250 return;
253 if (listen(d->s, SOMAXCONN) < 0) {
254 krb5_warn(kcm_context, errno, "listen %s", un.sun_path);
255 close(d->s);
256 d->s = -1;
257 return;
260 chmod(d->path, 0777);
262 return;
266 * Allocate descriptors for all the sockets that we should listen on
267 * and return the number of them.
270 static int
271 init_sockets(struct descr **desc)
273 struct descr *d;
274 size_t num = 0;
276 d = (struct descr *)malloc(sizeof(*d));
277 if (d == NULL) {
278 krb5_errx(kcm_context, 1, "malloc failed");
281 init_socket(d);
282 if (d->s != -1) {
283 kcm_log(5, "listening on domain socket %s", d->path);
284 num++;
287 reinit_descrs (d, num);
288 *desc = d;
290 return num;
294 * handle the request in `buf, len', from `addr' (or `from' as a string),
295 * sending a reply in `reply'.
298 static int
299 process_request(unsigned char *buf,
300 size_t len,
301 krb5_data *reply,
302 kcm_client *client)
304 krb5_data request;
306 if (len < 4) {
307 kcm_log(1, "malformed request from process %d (too short)",
308 client->pid);
309 return -1;
312 if (buf[0] != KCM_PROTOCOL_VERSION_MAJOR ||
313 buf[1] != KCM_PROTOCOL_VERSION_MINOR) {
314 kcm_log(1, "incorrect protocol version %d.%d from process %d",
315 buf[0], buf[1], client->pid);
316 return -1;
319 buf += 2;
320 len -= 2;
322 /* buf is now pointing at opcode */
324 request.data = buf;
325 request.length = len;
327 return kcm_dispatch(kcm_context, client, &request, reply);
331 * Handle the request in `buf, len' to socket `d'
334 static void
335 do_request(void *buf, size_t len, struct descr *d)
337 krb5_error_code ret;
338 krb5_data reply;
340 reply.length = 0;
342 ret = process_request(buf, len, &reply, &d->peercred);
343 if (reply.length != 0) {
344 unsigned char len[4];
345 struct msghdr msghdr;
346 struct iovec iov[2];
348 kcm_log(5, "sending %lu bytes to process %d",
349 (unsigned long)reply.length,
350 (int)d->peercred.pid);
352 memset (&msghdr, 0, sizeof(msghdr));
353 msghdr.msg_name = NULL;
354 msghdr.msg_namelen = 0;
355 msghdr.msg_iov = iov;
356 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
357 #if 0
358 msghdr.msg_control = NULL;
359 msghdr.msg_controllen = 0;
360 #endif
362 len[0] = (reply.length >> 24) & 0xff;
363 len[1] = (reply.length >> 16) & 0xff;
364 len[2] = (reply.length >> 8) & 0xff;
365 len[3] = reply.length & 0xff;
367 iov[0].iov_base = (void*)len;
368 iov[0].iov_len = 4;
369 iov[1].iov_base = reply.data;
370 iov[1].iov_len = reply.length;
372 if (sendmsg (d->s, &msghdr, 0) < 0) {
373 kcm_log (0, "sendmsg(%d): %d %s", (int)d->peercred.pid,
374 errno, strerror(errno));
375 krb5_data_free(&reply);
376 return;
379 krb5_data_free(&reply);
382 if (ret) {
383 kcm_log(0, "Failed processing %lu byte request from process %d",
384 (unsigned long)len, d->peercred.pid);
388 static void
389 clear_descr(struct descr *d)
391 if(d->buf)
392 memset(d->buf, 0, d->size);
393 d->len = 0;
394 if(d->s != -1)
395 close(d->s);
396 d->s = -1;
399 #define STREAM_TIMEOUT 4
402 * accept a new stream connection on `d[parent]' and store it in `d[child]'
405 static void
406 add_new_stream (struct descr *d, int parent, int child)
408 int s;
410 if (child == -1)
411 return;
413 d[child].peercred.pid = -1;
414 d[child].peercred.uid = -1;
415 d[child].peercred.gid = -1;
417 d[child].sock_len = sizeof(d[child].__ss);
418 s = accept(d[parent].s, d[child].sa, &d[child].sock_len);
419 if(s < 0) {
420 krb5_warn(kcm_context, errno, "accept");
421 return;
424 if (s >= FD_SETSIZE) {
425 krb5_warnx(kcm_context, "socket FD too large");
426 close (s);
427 return;
430 d[child].s = s;
431 d[child].timeout = time(NULL) + STREAM_TIMEOUT;
432 d[child].type = SOCK_STREAM;
436 * Grow `d' to handle at least `n'.
437 * Return != 0 if fails
440 static int
441 grow_descr (struct descr *d, size_t n)
443 if (d->size - d->len < n) {
444 unsigned char *tmp;
445 size_t grow;
447 grow = max(1024, d->len + n);
448 if (d->size + grow > max_request) {
449 kcm_log(0, "Request exceeds max request size (%lu bytes).",
450 (unsigned long)d->size + grow);
451 clear_descr(d);
452 return -1;
454 tmp = realloc (d->buf, d->size + grow);
455 if (tmp == NULL) {
456 kcm_log(0, "Failed to re-allocate %lu bytes.",
457 (unsigned long)d->size + grow);
458 clear_descr(d);
459 return -1;
461 d->size += grow;
462 d->buf = tmp;
464 return 0;
468 * Handle incoming data to the stream socket in `d[index]'
471 static void
472 handle_stream(struct descr *d, int index, int min_free)
474 unsigned char buf[1024];
475 int n;
476 int ret = 0;
478 if (d[index].timeout == 0) {
479 add_new_stream (d, index, min_free);
480 return;
483 if (update_client_creds(d[index].s, &d[index].peercred)) {
484 krb5_warnx(kcm_context, "failed to update peer identity");
485 clear_descr(d + index);
486 return;
489 if (d[index].peercred.uid == -1) {
490 krb5_warnx(kcm_context, "failed to determine peer identity");
491 clear_descr (d + index);
492 return;
495 n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL);
496 if (n < 0) {
497 krb5_warn(kcm_context, errno, "recvfrom");
498 return;
499 } else if (n == 0) {
500 krb5_warnx(kcm_context, "connection closed before end of data "
501 "after %lu bytes from process %ld",
502 (unsigned long) d[index].len, (long) d[index].peercred.pid);
503 clear_descr (d + index);
504 return;
506 if (grow_descr (&d[index], n))
507 return;
508 memcpy(d[index].buf + d[index].len, buf, n);
509 d[index].len += n;
510 if (d[index].len > 4) {
511 krb5_storage *sp;
512 int32_t len;
514 sp = krb5_storage_from_mem(d[index].buf, d[index].len);
515 if (sp == NULL) {
516 kcm_log (0, "krb5_storage_from_mem failed");
517 ret = -1;
518 } else {
519 krb5_ret_int32(sp, &len);
520 krb5_storage_free(sp);
521 if (d[index].len - 4 >= len) {
522 memmove(d[index].buf, d[index].buf + 4, d[index].len - 4);
523 ret = 1;
524 } else
525 ret = 0;
528 if (ret < 0)
529 return;
530 else if (ret == 1) {
531 do_request(d[index].buf, d[index].len, &d[index]);
532 clear_descr(d + index);
536 #ifdef HAVE_DOOR_CREATE
538 static void
539 kcm_door_server(void *cookie, char *argp, size_t arg_size,
540 door_desc_t *dp, uint_t n_desc)
542 kcm_client peercred;
543 door_cred_t cred;
544 krb5_error_code ret;
545 krb5_data reply;
546 size_t length;
547 char *p;
549 reply.length = 0;
551 p = NULL;
552 length = 0;
554 if (door_cred(&cred) != 0) {
555 kcm_log(0, "door_cred failed with %s", strerror(errno));
556 goto out;
559 peercred.uid = cred.dc_euid;
560 peercred.gid = cred.dc_egid;
561 peercred.pid = cred.dc_pid;
563 ret = process_request((unsigned char*)argp, arg_size, &reply, &peercred);
564 if (reply.length != 0) {
565 p = alloca(reply.length); /* XXX don't use alloca */
566 if (p) {
567 memcpy(p, reply.data, reply.length);
568 length = reply.length;
570 krb5_data_free(&reply);
573 out:
574 door_return(p, length, NULL, 0);
577 static void
578 kcm_setup_door(void)
580 int fd, ret;
581 char *path;
583 fd = door_create(kcm_door_server, NULL, 0);
584 if (fd < 0)
585 krb5_err(kcm_context, 1, errno, "Failed to create door");
587 if (door_path != NULL)
588 path = door_path;
589 else
590 path = _PATH_KCM_DOOR;
592 unlink(path);
593 ret = open(path, O_RDWR | O_CREAT, 0666);
594 if (ret < 0)
595 krb5_err(kcm_context, 1, errno, "Failed to create/open door");
596 close(ret);
598 ret = fattach(fd, path);
599 if (ret < 0)
600 krb5_err(kcm_context, 1, errno, "Failed to attach door");
603 #endif /* HAVE_DOOR_CREATE */
606 void
607 kcm_loop(void)
609 struct descr *d;
610 int ndescr;
612 #ifdef HAVE_DOOR_CREATE
613 kcm_setup_door();
614 #endif
616 ndescr = init_sockets(&d);
617 if (ndescr <= 0) {
618 krb5_warnx(kcm_context, "No sockets!");
619 #ifndef HAVE_DOOR_CREATE
620 exit(1);
621 #endif
623 while (exit_flag == 0){
624 struct timeval tmout;
625 fd_set fds;
626 int min_free = -1;
627 int max_fd = 0;
628 int i;
630 FD_ZERO(&fds);
631 for(i = 0; i < ndescr; i++) {
632 if (d[i].s >= 0){
633 if(d[i].type == SOCK_STREAM &&
634 d[i].timeout && d[i].timeout < time(NULL)) {
635 kcm_log(1, "Stream connection from %d expired after %lu bytes",
636 d[i].peercred.pid, (unsigned long)d[i].len);
637 clear_descr(&d[i]);
638 continue;
640 if (max_fd < d[i].s)
641 max_fd = d[i].s;
642 if (max_fd >= FD_SETSIZE)
643 krb5_errx(kcm_context, 1, "fd too large");
644 FD_SET(d[i].s, &fds);
645 } else if (min_free < 0 || i < min_free)
646 min_free = i;
648 if (min_free == -1) {
649 struct descr *tmp;
650 tmp = realloc(d, (ndescr + 4) * sizeof(*d));
651 if(tmp == NULL)
652 krb5_warnx(kcm_context, "No memory");
653 else {
654 d = tmp;
655 reinit_descrs (d, ndescr);
656 memset(d + ndescr, 0, 4 * sizeof(*d));
657 for(i = ndescr; i < ndescr + 4; i++)
658 init_descr (&d[i]);
659 min_free = ndescr;
660 ndescr += 4;
664 tmout.tv_sec = STREAM_TIMEOUT;
665 tmout.tv_usec = 0;
666 switch (select(max_fd + 1, &fds, 0, 0, &tmout)){
667 case 0:
668 kcm_run_events(kcm_context, time(NULL));
669 break;
670 case -1:
671 if (errno != EINTR)
672 krb5_warn(kcm_context, errno, "select");
673 break;
674 default:
675 for(i = 0; i < ndescr; i++) {
676 if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) {
677 if (d[i].type == SOCK_STREAM)
678 handle_stream(d, i, min_free);
681 kcm_run_events(kcm_context, time(NULL));
682 break;
685 if (d->path != NULL)
686 unlink(d->path);
687 free(d);