2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
36 __RCSID("$Heimdal: connect.c 16314 2005-11-29 19:03:50Z lha $"
47 struct sockaddr_storage __ss
;
54 init_descr(struct descr
*d
)
56 memset(d
, 0, sizeof(*d
));
57 d
->sa
= (struct sockaddr
*)&d
->__ss
;
62 * re-initialize all `n' ->sa in `d'.
66 reinit_descrs (struct descr
*d
, int n
)
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.
83 update_client_creds(int s
, kcm_client
*peer
)
90 if (getpeerucred(s
, &peercred
) != 0) {
91 peer
->uid
= ucred_geteuid(peercred
);
92 peer
->gid
= ucred_getegid(peercred
);
100 /* FreeBSD, OpenBSD */
105 if (getpeereid(s
, &uid
, &gid
) == 0) {
117 socklen_t pclen
= sizeof(pc
);
119 if (getsockopt(s
, SOL_SOCKET
, SO_PEERCRED
, (void *)&pc
, &pclen
) == 0) {
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
;
143 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
145 if (peer
->uid
== -1) {
152 memset(&msg
, 0, sizeof(msg
));
153 crmsgsize
= CMSG_SPACE(SOCKCREDSIZE(NGROUPS
));
157 crmsg
= malloc(crmsgsize
);
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) {
168 goto failed_scm_creds
;
171 if (msg
.msg_controllen
== 0 || (msg
.msg_flags
& MSG_CTRUNC
) != 0) {
173 goto failed_scm_creds
;
176 cmp
= CMSG_FIRSTHDR(&msg
);
177 if (cmp
->cmsg_level
!= SOL_SOCKET
|| cmp
->cmsg_type
!= SCM_CREDS
) {
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
;
191 /* we already got the cred, just return it */
196 krb5_warn(kcm_context
, errno
, "failed to determine peer identity");
202 * Create the socket (family, type, port) in `d'
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
);
214 un
.sun_family
= AF_UNIX
;
216 if (socket_path
!= NULL
)
217 d
->path
= socket_path
;
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);
225 krb5_warn(kcm_context
, errno
, "socket(%d, %d, 0)", AF_UNIX
, SOCK_STREAM
);
229 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
232 setsockopt(d
->s
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&one
, sizeof(one
));
238 setsockopt(d
->s
, 0, LOCAL_CREDS
, (void *)&one
, sizeof(one
));
242 d
->type
= SOCK_STREAM
;
246 if (bind(d
->s
, sa
, sa_size
) < 0) {
247 krb5_warn(kcm_context
, errno
, "bind %s", un
.sun_path
);
253 if (listen(d
->s
, SOMAXCONN
) < 0) {
254 krb5_warn(kcm_context
, errno
, "listen %s", un
.sun_path
);
260 chmod(d
->path
, 0777);
266 * Allocate descriptors for all the sockets that we should listen on
267 * and return the number of them.
271 init_sockets(struct descr
**desc
)
276 d
= (struct descr
*)malloc(sizeof(*d
));
278 krb5_errx(kcm_context
, 1, "malloc failed");
283 kcm_log(5, "listening on domain socket %s", d
->path
);
287 reinit_descrs (d
, num
);
294 * handle the request in `buf, len', from `addr' (or `from' as a string),
295 * sending a reply in `reply'.
299 process_request(unsigned char *buf
,
307 kcm_log(1, "malformed request from process %d (too short)",
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
);
322 /* buf is now pointing at opcode */
325 request
.length
= len
;
327 return kcm_dispatch(kcm_context
, client
, &request
, reply
);
331 * Handle the request in `buf, len' to socket `d'
335 do_request(void *buf
, size_t len
, struct descr
*d
)
342 ret
= process_request(buf
, len
, &reply
, &d
->peercred
);
343 if (reply
.length
!= 0) {
344 unsigned char len
[4];
345 struct msghdr msghdr
;
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
);
358 msghdr
.msg_control
= NULL
;
359 msghdr
.msg_controllen
= 0;
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
;
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
);
379 krb5_data_free(&reply
);
383 kcm_log(0, "Failed processing %lu byte request from process %d",
384 (unsigned long)len
, d
->peercred
.pid
);
389 clear_descr(struct descr
*d
)
392 memset(d
->buf
, 0, d
->size
);
399 #define STREAM_TIMEOUT 4
402 * accept a new stream connection on `d[parent]' and store it in `d[child]'
406 add_new_stream (struct descr
*d
, int parent
, int child
)
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
);
420 krb5_warn(kcm_context
, errno
, "accept");
424 if (s
>= FD_SETSIZE
) {
425 krb5_warnx(kcm_context
, "socket FD too large");
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
441 grow_descr (struct descr
*d
, size_t n
)
443 if (d
->size
- d
->len
< n
) {
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
);
454 tmp
= realloc (d
->buf
, d
->size
+ grow
);
456 kcm_log(0, "Failed to re-allocate %lu bytes.",
457 (unsigned long)d
->size
+ grow
);
468 * Handle incoming data to the stream socket in `d[index]'
472 handle_stream(struct descr
*d
, int index
, int min_free
)
474 unsigned char buf
[1024];
478 if (d
[index
].timeout
== 0) {
479 add_new_stream (d
, index
, min_free
);
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
);
489 if (d
[index
].peercred
.uid
== -1) {
490 krb5_warnx(kcm_context
, "failed to determine peer identity");
491 clear_descr (d
+ index
);
495 n
= recvfrom(d
[index
].s
, buf
, sizeof(buf
), 0, NULL
, NULL
);
497 krb5_warn(kcm_context
, errno
, "recvfrom");
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
);
506 if (grow_descr (&d
[index
], n
))
508 memcpy(d
[index
].buf
+ d
[index
].len
, buf
, n
);
510 if (d
[index
].len
> 4) {
514 sp
= krb5_storage_from_mem(d
[index
].buf
, d
[index
].len
);
516 kcm_log (0, "krb5_storage_from_mem failed");
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);
531 do_request(d
[index
].buf
, d
[index
].len
, &d
[index
]);
532 clear_descr(d
+ index
);
536 #ifdef HAVE_DOOR_CREATE
539 kcm_door_server(void *cookie
, char *argp
, size_t arg_size
,
540 door_desc_t
*dp
, uint_t n_desc
)
554 if (door_cred(&cred
) != 0) {
555 kcm_log(0, "door_cred failed with %s", strerror(errno
));
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 */
567 memcpy(p
, reply
.data
, reply
.length
);
568 length
= reply
.length
;
570 krb5_data_free(&reply
);
574 door_return(p
, length
, NULL
, 0);
583 fd
= door_create(kcm_door_server
, NULL
, 0);
585 krb5_err(kcm_context
, 1, errno
, "Failed to create door");
587 if (door_path
!= NULL
)
590 path
= _PATH_KCM_DOOR
;
593 ret
= open(path
, O_RDWR
| O_CREAT
, 0666);
595 krb5_err(kcm_context
, 1, errno
, "Failed to create/open door");
598 ret
= fattach(fd
, path
);
600 krb5_err(kcm_context
, 1, errno
, "Failed to attach door");
603 #endif /* HAVE_DOOR_CREATE */
612 #ifdef HAVE_DOOR_CREATE
616 ndescr
= init_sockets(&d
);
618 krb5_warnx(kcm_context
, "No sockets!");
619 #ifndef HAVE_DOOR_CREATE
623 while (exit_flag
== 0){
624 struct timeval tmout
;
631 for(i
= 0; i
< ndescr
; i
++) {
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
);
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
)
648 if (min_free
== -1) {
650 tmp
= realloc(d
, (ndescr
+ 4) * sizeof(*d
));
652 krb5_warnx(kcm_context
, "No memory");
655 reinit_descrs (d
, ndescr
);
656 memset(d
+ ndescr
, 0, 4 * sizeof(*d
));
657 for(i
= ndescr
; i
< ndescr
+ 4; i
++)
664 tmout
.tv_sec
= STREAM_TIMEOUT
;
666 switch (select(max_fd
+ 1, &fds
, 0, 0, &tmout
)){
668 kcm_run_events(kcm_context
, time(NULL
));
672 krb5_warn(kcm_context
, errno
, "select");
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
));