handle unix credentials

This commit is contained in:
Love Hornquist Astrand
2010-10-06 23:32:36 -07:00
parent f225af82c1
commit a3746c9f07
2 changed files with 161 additions and 5 deletions

View File

@@ -49,6 +49,10 @@
#include <krb5-types.h> #include <krb5-types.h>
#include <asn1-common.h> #include <asn1-common.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <base64.h> #include <base64.h>
#include <heim-ipc.h> #include <heim-ipc.h>

View File

@@ -456,6 +456,7 @@ struct client {
#define INHERIT_MASK 0xffff0000 #define INHERIT_MASK 0xffff0000
#define INCLUDE_ERROR_CODE (1 << 16) #define INCLUDE_ERROR_CODE (1 << 16)
#define ALLOW_HTTP (1<<17) #define ALLOW_HTTP (1<<17)
#define UNIX_SOCKET (1<<18)
unsigned calls; unsigned calls;
size_t ptr, len; size_t ptr, len;
uint8_t *inmsg; uint8_t *inmsg;
@@ -465,6 +466,11 @@ struct client {
dispatch_source_t in; dispatch_source_t in;
dispatch_source_t out; dispatch_source_t out;
#endif #endif
struct {
uid_t uid;
gid_t gid;
pid_t pid;
} unixrights;
}; };
#ifndef HAVE_GCD #ifndef HAVE_GCD
@@ -476,6 +482,132 @@ static void handle_read(struct client *);
static void handle_write(struct client *); static void handle_write(struct client *);
static int maybe_close(struct client *); static int maybe_close(struct client *);
/*
* Update peer credentials from socket.
*
* SCM_CREDS can only be updated the first time there is read data to
* read from the filedescriptor, so if we read do it before this
* point, the cred data might not be is not there yet.
*/
static int
update_client_creds(struct client *c)
{
#ifdef HAVE_GETPEERUCRED
/* Solaris 10 */
{
ucred_t *peercred;
if (getpeerucred(c->fd, &peercred) != 0) {
c->unixrights.uid = ucred_geteuid(peercred);
c->unixrights.gid = ucred_getegid(peercred);
c->unixrights.pid = 0;
ucred_free(peercred);
return 1;
}
}
#endif
#ifdef HAVE_GETPEEREID
/* FreeBSD, OpenBSD */
{
uid_t uid;
gid_t gid;
if (getpeereid(c->fd, &uid, &gid) == 0) {
c->unixrights.uid = uid;
c->unixrights.gid = gid;
c->unixrights.pid = 0;
return 1;
}
}
#endif
#ifdef SO_PEERCRED
/* Linux */
{
struct ucred pc;
socklen_t pclen = sizeof(pc);
if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
c->unixrights.uid = pc.uid;
c->unixrights.gid = pc.gid;
c->unixrights.pid = pc.pid;
return 1;
}
}
#endif
#if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
{
struct xucred peercred;
socklen_t peercredlen = sizeof(peercred);
if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
(void *)&peercred, &peercredlen) == 0
&& peercred.cr_version == XUCRED_VERSION)
{
c->unixrights.uid = peercred.cr_uid;
c->unixrights.gid = peercred.cr_gid;
c->unixrights.pid = 0;
return 1;
}
}
#endif
#if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
/* NetBSD */
if (c->unixrights.uid == -1) {
struct msghdr msg;
socklen_t crmsgsize;
void *crmsg;
struct cmsghdr *cmp;
struct sockcred *sc;
memset(&msg, 0, sizeof(msg));
crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
if (crmsgsize == 0)
return 1 ;
crmsg = malloc(crmsgsize);
if (crmsg == NULL)
goto failed_scm_creds;
memset(crmsg, 0, crmsgsize);
msg.msg_control = crmsg;
msg.msg_controllen = crmsgsize;
if (recvmsg(c->fd, &msg, 0) < 0) {
free(crmsg);
goto failed_scm_creds;
}
if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
free(crmsg);
goto failed_scm_creds;
}
cmp = CMSG_FIRSTHDR(&msg);
if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
free(crmsg);
goto failed_scm_creds;
}
sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
c->unixrights.uid = sc->sc_euid;
c->unixrights.gid = sc->sc_egid;
c->unixrights.pid = 0;
free(crmsg);
return 1;
} else {
/* we already got the cred, just return it */
return 1;
}
failed_scm_creds:
#endif
return 0;
}
static struct client * static struct client *
add_new_socket(int fd, add_new_socket(int fd,
int flags, int flags,
@@ -569,6 +701,7 @@ maybe_close(struct client *c)
struct socket_call { struct socket_call {
heim_idata in; heim_idata in;
struct client *c; struct client *c;
heim_icred cred;
}; };
static void static void
@@ -616,7 +749,8 @@ socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
} }
c->calls--; c->calls--;
if (sc->cred)
heim_ipc_free_cred(sc->cred);
free(sc->in.data); free(sc->in.data);
sc->c = NULL; /* so we can catch double complete */ sc->c = NULL; /* so we can catch double complete */
free(sc); free(sc);
@@ -799,8 +933,15 @@ handle_read(struct client *c)
} }
c->calls++; c->calls++;
if ((c->flags & UNIX_SOCKET) != 0) {
if (update_client_creds(c))
_heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
c->unixrights.pid, -1, &cs->cred);
}
c->callback(c->userctx, &cs->in, c->callback(c->userctx, &cs->in,
NULL, socket_complete, cs->cred, socket_complete,
(heim_sipc_call)cs); (heim_sipc_call)cs);
} }
} }
@@ -924,6 +1065,11 @@ heim_sipc_stream_listener(int fd, int type,
ct->mech = c; ct->mech = c;
ct->release = socket_release; ct->release = socket_release;
c->unixrights.uid = (uid_t) -1;
c->unixrights.gid = (gid_t) -1;
c->unixrights.pid = (pid_t) 0;
*ctx = ct; *ctx = ct;
return 0; return 0;
} }
@@ -934,7 +1080,7 @@ heim_sipc_service_unix(const char *service,
void *user, heim_sipc *ctx) void *user, heim_sipc *ctx)
{ {
struct sockaddr_un un; struct sockaddr_un un;
int fd; int fd, ret;
un.sun_family = AF_UNIX; un.sun_family = AF_UNIX;
@@ -966,8 +1112,14 @@ heim_sipc_service_unix(const char *service,
chmod(un.sun_path, 0666); chmod(un.sun_path, 0666);
return heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC, ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
callback, user, ctx); callback, user, ctx);
if (ret == 0) {
struct client *c = (*ctx)->mech;
c->flags |= UNIX_SOCKET;
}
return ret;
} }
/** /**