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
46 struct sockaddr_storage __ss
;
53 init_descr(struct descr
*d
)
55 memset(d
, 0, sizeof(*d
));
56 d
->sa
= (struct sockaddr
*)&d
->__ss
;
61 * re-initialize all `n' ->sa in `d'.
65 reinit_descrs (struct descr
*d
, int n
)
69 for (i
= 0; i
< n
; ++i
)
70 d
[i
].sa
= (struct sockaddr
*)&d
[i
].__ss
;
74 * Update peer credentials from socket.
76 * SCM_CREDS can only be updated the first time there is read data to
77 * read from the filedescriptor, so if we read do it before this
78 * point, the cred data might not be is not there yet.
82 update_client_creds(int s
, kcm_client
*peer
)
89 if (getpeerucred(s
, &peercred
) != 0) {
90 peer
->uid
= ucred_geteuid(peercred
);
91 peer
->gid
= ucred_getegid(peercred
);
99 /* FreeBSD, OpenBSD */
104 if (getpeereid(s
, &uid
, &gid
) == 0) {
116 socklen_t pclen
= sizeof(pc
);
118 if (getsockopt(s
, SOL_SOCKET
, SO_PEERCRED
, (void *)&pc
, &pclen
) == 0) {
126 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
128 struct xucred peercred
;
129 socklen_t peercredlen
= sizeof(peercred
);
131 if (getsockopt(s
, LOCAL_PEERCRED
, 1,
132 (void *)&peercred
, &peercredlen
) == 0
133 && peercred
.cr_version
== XUCRED_VERSION
)
135 peer
->uid
= peercred
.cr_uid
;
136 peer
->gid
= peercred
.cr_gid
;
142 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
144 if (peer
->uid
== -1) {
151 memset(&msg
, 0, sizeof(msg
));
152 crmsgsize
= CMSG_SPACE(SOCKCREDSIZE(NGROUPS
));
156 crmsg
= malloc(crmsgsize
);
158 goto failed_scm_creds
;
160 memset(crmsg
, 0, crmsgsize
);
162 msg
.msg_control
= crmsg
;
163 msg
.msg_controllen
= crmsgsize
;
165 if (recvmsg(s
, &msg
, 0) < 0) {
167 goto failed_scm_creds
;
170 if (msg
.msg_controllen
== 0 || (msg
.msg_flags
& MSG_CTRUNC
) != 0) {
172 goto failed_scm_creds
;
175 cmp
= CMSG_FIRSTHDR(&msg
);
176 if (cmp
->cmsg_level
!= SOL_SOCKET
|| cmp
->cmsg_type
!= SCM_CREDS
) {
178 goto failed_scm_creds
;
181 sc
= (struct sockcred
*)(void *)CMSG_DATA(cmp
);
183 peer
->uid
= sc
->sc_euid
;
184 peer
->gid
= sc
->sc_egid
;
190 /* we already got the cred, just return it */
195 krb5_warn(kcm_context
, errno
, "failed to determine peer identity");
201 * Create the socket (family, type, port) in `d'
205 init_socket(struct descr
*d
)
207 struct sockaddr_un un
;
208 struct sockaddr
*sa
= (struct sockaddr
*)&un
;
209 krb5_socklen_t sa_size
= sizeof(un
);
213 un
.sun_family
= AF_UNIX
;
215 if (socket_path
!= NULL
)
216 d
->path
= socket_path
;
218 d
->path
= _PATH_KCM_SOCKET
;
220 strlcpy(un
.sun_path
, d
->path
, sizeof(un
.sun_path
));
222 d
->s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
224 krb5_warn(kcm_context
, errno
, "socket(%d, %d, 0)", AF_UNIX
, SOCK_STREAM
);
228 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
231 setsockopt(d
->s
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&one
, sizeof(one
));
237 setsockopt(d
->s
, 0, LOCAL_CREDS
, (void *)&one
, sizeof(one
));
241 d
->type
= SOCK_STREAM
;
245 if (bind(d
->s
, sa
, sa_size
) < 0) {
246 krb5_warn(kcm_context
, errno
, "bind %s", un
.sun_path
);
252 if (listen(d
->s
, SOMAXCONN
) < 0) {
253 krb5_warn(kcm_context
, errno
, "listen %s", un
.sun_path
);
259 chmod(d
->path
, 0777);
265 * Allocate descriptors for all the sockets that we should listen on
266 * and return the number of them.
270 init_sockets(struct descr
**desc
)
275 d
= (struct descr
*)malloc(sizeof(*d
));
277 krb5_errx(kcm_context
, 1, "malloc failed");
282 kcm_log(5, "listening on domain socket %s", d
->path
);
286 reinit_descrs (d
, num
);
293 * handle the request in `buf, len', from `addr' (or `from' as a string),
294 * sending a reply in `reply'.
298 process_request(unsigned char *buf
,
306 kcm_log(1, "malformed request from process %d (too short)",
311 if (buf
[0] != KCM_PROTOCOL_VERSION_MAJOR
||
312 buf
[1] != KCM_PROTOCOL_VERSION_MINOR
) {
313 kcm_log(1, "incorrect protocol version %d.%d from process %d",
314 buf
[0], buf
[1], client
->pid
);
321 /* buf is now pointing at opcode */
324 request
.length
= len
;
326 return kcm_dispatch(kcm_context
, client
, &request
, reply
);
330 * Handle the request in `buf, len' to socket `d'
334 do_request(void *buf
, size_t len
, struct descr
*d
)
341 ret
= process_request(buf
, len
, &reply
, &d
->peercred
);
342 if (reply
.length
!= 0) {
343 unsigned char len
[4];
344 struct msghdr msghdr
;
347 kcm_log(5, "sending %lu bytes to process %d",
348 (unsigned long)reply
.length
,
349 (int)d
->peercred
.pid
);
351 memset (&msghdr
, 0, sizeof(msghdr
));
352 msghdr
.msg_name
= NULL
;
353 msghdr
.msg_namelen
= 0;
354 msghdr
.msg_iov
= iov
;
355 msghdr
.msg_iovlen
= sizeof(iov
)/sizeof(*iov
);
357 msghdr
.msg_control
= NULL
;
358 msghdr
.msg_controllen
= 0;
361 len
[0] = (reply
.length
>> 24) & 0xff;
362 len
[1] = (reply
.length
>> 16) & 0xff;
363 len
[2] = (reply
.length
>> 8) & 0xff;
364 len
[3] = reply
.length
& 0xff;
366 iov
[0].iov_base
= (void*)len
;
368 iov
[1].iov_base
= reply
.data
;
369 iov
[1].iov_len
= reply
.length
;
371 if (sendmsg (d
->s
, &msghdr
, 0) < 0) {
372 kcm_log (0, "sendmsg(%d): %d %s", (int)d
->peercred
.pid
,
373 errno
, strerror(errno
));
374 krb5_data_free(&reply
);
378 krb5_data_free(&reply
);
382 kcm_log(0, "Failed processing %lu byte request from process %d",
383 (unsigned long)len
, d
->peercred
.pid
);
388 clear_descr(struct descr
*d
)
391 memset(d
->buf
, 0, d
->size
);
398 #define STREAM_TIMEOUT 4
401 * accept a new stream connection on `d[parent]' and store it in `d[child]'
405 add_new_stream (struct descr
*d
, int parent
, int child
)
412 d
[child
].peercred
.pid
= -1;
413 d
[child
].peercred
.uid
= -1;
414 d
[child
].peercred
.gid
= -1;
416 d
[child
].sock_len
= sizeof(d
[child
].__ss
);
417 s
= accept(d
[parent
].s
, d
[child
].sa
, &d
[child
].sock_len
);
419 krb5_warn(kcm_context
, errno
, "accept");
423 if (s
>= FD_SETSIZE
) {
424 krb5_warnx(kcm_context
, "socket FD too large");
430 d
[child
].timeout
= time(NULL
) + STREAM_TIMEOUT
;
431 d
[child
].type
= SOCK_STREAM
;
435 * Grow `d' to handle at least `n'.
436 * Return != 0 if fails
440 grow_descr (struct descr
*d
, size_t n
)
442 if (d
->size
- d
->len
< n
) {
446 grow
= max(1024, d
->len
+ n
);
447 if (d
->size
+ grow
> max_request
) {
448 kcm_log(0, "Request exceeds max request size (%lu bytes).",
449 (unsigned long)d
->size
+ grow
);
453 tmp
= realloc (d
->buf
, d
->size
+ grow
);
455 kcm_log(0, "Failed to re-allocate %lu bytes.",
456 (unsigned long)d
->size
+ grow
);
467 * Handle incoming data to the stream socket in `d[index]'
471 handle_stream(struct descr
*d
, int index
, int min_free
)
473 unsigned char buf
[1024];
477 if (d
[index
].timeout
== 0) {
478 add_new_stream (d
, index
, min_free
);
482 if (update_client_creds(d
[index
].s
, &d
[index
].peercred
)) {
483 krb5_warnx(kcm_context
, "failed to update peer identity");
484 clear_descr(d
+ index
);
488 if (d
[index
].peercred
.uid
== -1) {
489 krb5_warnx(kcm_context
, "failed to determine peer identity");
490 clear_descr (d
+ index
);
494 n
= recvfrom(d
[index
].s
, buf
, sizeof(buf
), 0, NULL
, NULL
);
496 krb5_warn(kcm_context
, errno
, "recvfrom");
499 krb5_warnx(kcm_context
, "connection closed before end of data "
500 "after %lu bytes from process %ld",
501 (unsigned long) d
[index
].len
, (long) d
[index
].peercred
.pid
);
502 clear_descr (d
+ index
);
505 if (grow_descr (&d
[index
], n
))
507 memcpy(d
[index
].buf
+ d
[index
].len
, buf
, n
);
509 if (d
[index
].len
> 4) {
513 sp
= krb5_storage_from_mem(d
[index
].buf
, d
[index
].len
);
515 kcm_log (0, "krb5_storage_from_mem failed");
518 krb5_ret_int32(sp
, &len
);
519 krb5_storage_free(sp
);
520 if (d
[index
].len
- 4 >= len
) {
521 memmove(d
[index
].buf
, d
[index
].buf
+ 4, d
[index
].len
- 4);
530 do_request(d
[index
].buf
, d
[index
].len
, &d
[index
]);
531 clear_descr(d
+ index
);
535 #ifdef HAVE_DOOR_CREATE
538 kcm_door_server(void *cookie
, char *argp
, size_t arg_size
,
539 door_desc_t
*dp
, uint_t n_desc
)
553 if (door_cred(&cred
) != 0) {
554 kcm_log(0, "door_cred failed with %s", strerror(errno
));
558 peercred
.uid
= cred
.dc_euid
;
559 peercred
.gid
= cred
.dc_egid
;
560 peercred
.pid
= cred
.dc_pid
;
562 ret
= process_request((unsigned char*)argp
, arg_size
, &reply
, &peercred
);
563 if (reply
.length
!= 0) {
564 p
= alloca(reply
.length
); /* XXX don't use alloca */
566 memcpy(p
, reply
.data
, reply
.length
);
567 length
= reply
.length
;
569 krb5_data_free(&reply
);
573 door_return(p
, length
, NULL
, 0);
582 fd
= door_create(kcm_door_server
, NULL
, 0);
584 krb5_err(kcm_context
, 1, errno
, "Failed to create door");
586 if (door_path
!= NULL
)
589 path
= _PATH_KCM_DOOR
;
592 ret
= open(path
, O_RDWR
| O_CREAT
, 0666);
594 krb5_err(kcm_context
, 1, errno
, "Failed to create/open door");
597 ret
= fattach(fd
, path
);
599 krb5_err(kcm_context
, 1, errno
, "Failed to attach door");
602 #endif /* HAVE_DOOR_CREATE */
611 #ifdef HAVE_DOOR_CREATE
615 ndescr
= init_sockets(&d
);
617 krb5_warnx(kcm_context
, "No sockets!");
618 #ifndef HAVE_DOOR_CREATE
622 while (exit_flag
== 0){
623 struct timeval tmout
;
630 for(i
= 0; i
< ndescr
; i
++) {
632 if(d
[i
].type
== SOCK_STREAM
&&
633 d
[i
].timeout
&& d
[i
].timeout
< time(NULL
)) {
634 kcm_log(1, "Stream connection from %d expired after %lu bytes",
635 d
[i
].peercred
.pid
, (unsigned long)d
[i
].len
);
641 if (max_fd
>= FD_SETSIZE
)
642 krb5_errx(kcm_context
, 1, "fd too large");
643 FD_SET(d
[i
].s
, &fds
);
644 } else if (min_free
< 0 || i
< min_free
)
647 if (min_free
== -1) {
649 tmp
= realloc(d
, (ndescr
+ 4) * sizeof(*d
));
651 krb5_warnx(kcm_context
, "No memory");
654 reinit_descrs (d
, ndescr
);
655 memset(d
+ ndescr
, 0, 4 * sizeof(*d
));
656 for(i
= ndescr
; i
< ndescr
+ 4; i
++)
663 tmout
.tv_sec
= STREAM_TIMEOUT
;
665 switch (select(max_fd
+ 1, &fds
, 0, 0, &tmout
)){
667 kcm_run_events(kcm_context
, time(NULL
));
671 krb5_warn(kcm_context
, errno
, "select");
674 for(i
= 0; i
< ndescr
; i
++) {
675 if(d
[i
].s
>= 0 && FD_ISSET(d
[i
].s
, &fds
)) {
676 if (d
[i
].type
== SOCK_STREAM
)
677 handle_stream(d
, i
, min_free
);
680 kcm_run_events(kcm_context
, time(NULL
));