diff --git a/egg/egg-buffer.c b/egg/egg-buffer.c
new file mode 100644
index 0000000..f20588f
--- /dev/null
+++ b/egg/egg-buffer.c
@@ -0,0 +1,581 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-buffer.c - Generic data buffer, used by openssh, gnome-keyring
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ .
+
+ Author: Stef Walter
+*/
+#include "config.h"
+
+#include
+#include
+
+#include "egg-buffer.h"
+
+#define DEFAULT_ALLOCATOR ((EggBufferAllocator)realloc)
+
+int
+egg_buffer_init (EggBuffer *buffer, size_t reserve)
+{
+ return egg_buffer_init_full (buffer, reserve, NULL);
+}
+
+int
+egg_buffer_init_full (EggBuffer *buffer, size_t reserve, EggBufferAllocator allocator)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+ if (reserve == 0)
+ reserve = 64;
+
+ buffer->buf = (allocator) (NULL, reserve);
+ if (!buffer->buf) {
+ buffer->failures++;
+ return 0;
+ }
+
+ buffer->len = 0;
+ buffer->allocated_len = reserve;
+ buffer->failures = 0;
+ buffer->allocator = allocator;
+
+ return 1;
+}
+
+void
+egg_buffer_init_static (EggBuffer* buffer, const unsigned char *buf, size_t len)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ buffer->buf = (unsigned char*)buf;
+ buffer->len = len;
+ buffer->allocated_len = len;
+ buffer->failures = 0;
+
+ /* A null allocator, and the buffer can't change in size */
+ buffer->allocator = NULL;
+}
+
+void
+egg_buffer_init_allocated (EggBuffer *buffer, unsigned char *buf, size_t len,
+ EggBufferAllocator allocator)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ buffer->buf = buf;
+ buffer->len = len;
+ buffer->allocated_len = len;
+ buffer->failures = 0;
+ buffer->allocator = allocator;
+}
+
+void
+egg_buffer_reset (EggBuffer *buffer)
+{
+ memset (buffer->buf, 0, buffer->allocated_len);
+ buffer->len = 0;
+ buffer->failures = 0;
+}
+
+void
+egg_buffer_uninit (EggBuffer *buffer)
+{
+ if (!buffer)
+ return;
+
+ /*
+ * Free the memory block using allocator. If no allocator,
+ * then this memory is ownerd elsewhere and not to be freed.
+ */
+ if (buffer->buf && buffer->allocator)
+ (buffer->allocator) (buffer->buf, 0);
+
+ memset (buffer, 0, sizeof (*buffer));
+}
+
+unsigned char*
+egg_buffer_uninit_steal (EggBuffer *buffer, size_t *n_result)
+{
+ unsigned char *result;
+
+ if (n_result)
+ *n_result = buffer->len;
+ result = buffer->buf;
+
+ memset (buffer, 0, sizeof (*buffer));
+
+ return result;
+}
+
+int
+egg_buffer_set_allocator (EggBuffer *buffer, EggBufferAllocator allocator)
+{
+ unsigned char *buf = NULL;
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+ if (buffer->allocator == allocator)
+ return 1;
+
+ if (buffer->allocated_len) {
+ /* Reallocate memory block using new allocator */
+ buf = (allocator) (NULL, buffer->allocated_len);
+ if (buf == NULL)
+ return 0;
+
+ /* Copy stuff into new memory */
+ memcpy (buf, buffer->buf, buffer->allocated_len);
+ }
+
+ /* If old wasn't static, then free it */
+ if (buffer->allocator && buffer->buf)
+ (buffer->allocator) (buffer->buf, 0);
+
+ buffer->buf = buf;
+ buffer->allocator = allocator;
+
+ return 1;
+}
+
+int
+egg_buffer_equal (EggBuffer *b1, EggBuffer *b2)
+{
+ if (b1->len != b2->len)
+ return 0;
+ return memcmp (b1->buf, b2->buf, b1->len) == 0;
+}
+
+int
+egg_buffer_reserve (EggBuffer *buffer, size_t len)
+{
+ unsigned char *newbuf;
+ size_t newlen;
+
+ if (len < buffer->allocated_len)
+ return 1;
+
+ /* Calculate a new length, minimize number of buffer allocations */
+ newlen = buffer->allocated_len * 2;
+ if (len > newlen)
+ newlen += len;
+
+ /* Memory owned elsewhere can't be reallocated */
+ if (!buffer->allocator) {
+ buffer->failures++;
+ return 0;
+ }
+
+ /* Reallocate built in buffer using allocator */
+ newbuf = (buffer->allocator) (buffer->buf, newlen);
+ if (!newbuf) {
+ buffer->failures++;
+ return 0;
+ }
+
+ buffer->buf = newbuf;
+ buffer->allocated_len = newlen;
+
+ return 1;
+}
+
+int
+egg_buffer_resize (EggBuffer *buffer, size_t len)
+{
+ if (!egg_buffer_reserve (buffer, len))
+ return 0;
+
+ buffer->len = len;
+ return 1;
+}
+
+unsigned char*
+egg_buffer_add_empty (EggBuffer *buffer, size_t len)
+{
+ size_t pos = buffer->len;
+ if (!egg_buffer_reserve (buffer, buffer->len + len))
+ return NULL;
+ buffer->len += len;
+ return buffer->buf + pos;
+}
+
+int
+egg_buffer_append (EggBuffer *buffer, const unsigned char *val,
+ size_t len)
+{
+ if (!egg_buffer_reserve (buffer, buffer->len + len))
+ return 0; /* failures already incremented */
+ memcpy (buffer->buf + buffer->len, val, len);
+ buffer->len += len;
+ return 1;
+}
+
+int
+egg_buffer_add_byte (EggBuffer *buffer, unsigned char val)
+{
+ if (!egg_buffer_reserve (buffer, buffer->len + 1))
+ return 0; /* failures already incremented */
+ buffer->buf[buffer->len] = val;
+ buffer->len++;
+ return 1;
+}
+
+int
+egg_buffer_get_byte (EggBuffer *buffer, size_t offset,
+ size_t *next_offset, unsigned char *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 1 || offset > buffer->len - 1) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = *ptr;
+ if (next_offset != NULL)
+ *next_offset = offset + 1;
+ return 1;
+}
+
+void
+egg_buffer_encode_uint16 (unsigned char* buf, uint16_t val)
+{
+ buf[0] = (val >> 8) & 0xff;
+ buf[1] = (val >> 0) & 0xff;
+}
+
+uint16_t
+egg_buffer_decode_uint16 (unsigned char* buf)
+{
+ uint16_t val = buf[0] << 8 | buf[1];
+ return val;
+}
+
+int
+egg_buffer_add_uint16 (EggBuffer *buffer, uint16_t val)
+{
+ if (!egg_buffer_reserve (buffer, buffer->len + 2))
+ return 0; /* failures already incremented */
+ buffer->len += 2;
+ egg_buffer_set_uint16 (buffer, buffer->len - 2, val);
+ return 1;
+}
+
+int
+egg_buffer_set_uint16 (EggBuffer *buffer, size_t offset, uint16_t val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 2 || offset > buffer->len - 2) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ egg_buffer_encode_uint16 (ptr, val);
+ return 1;
+}
+
+int
+egg_buffer_get_uint16 (EggBuffer *buffer, size_t offset,
+ size_t *next_offset, uint16_t *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 2 || offset > buffer->len - 2) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = egg_buffer_decode_uint16 (ptr);
+ if (next_offset != NULL)
+ *next_offset = offset + 2;
+ return 1;
+}
+
+void
+egg_buffer_encode_uint32 (unsigned char* buf, uint32_t val)
+{
+ buf[0] = (val >> 24) & 0xff;
+ buf[1] = (val >> 16) & 0xff;
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = (val >> 0) & 0xff;
+}
+
+uint32_t
+egg_buffer_decode_uint32 (unsigned char* ptr)
+{
+ uint32_t val = (uint32_t) ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+ return val;
+}
+
+int
+egg_buffer_add_uint32 (EggBuffer *buffer, uint32_t val)
+{
+ if (!egg_buffer_reserve (buffer, buffer->len + 4))
+ return 0; /* failures already incremented */
+ buffer->len += 4;
+ egg_buffer_set_uint32 (buffer, buffer->len - 4, val);
+ return 1;
+}
+
+int
+egg_buffer_set_uint32 (EggBuffer *buffer, size_t offset, uint32_t val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 4 || offset > buffer->len - 4) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ egg_buffer_encode_uint32 (ptr, val);
+ return 1;
+}
+
+int
+egg_buffer_get_uint32 (EggBuffer *buffer, size_t offset, size_t *next_offset,
+ uint32_t *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 4 || offset > buffer->len - 4) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = egg_buffer_decode_uint32 (ptr);
+ if (next_offset != NULL)
+ *next_offset = offset + 4;
+ return 1;
+}
+
+int
+egg_buffer_add_uint64 (EggBuffer *buffer, uint64_t val)
+{
+ if (!egg_buffer_add_uint32 (buffer, ((val >> 32) & 0xffffffff)))
+ return 0;
+ return egg_buffer_add_uint32 (buffer, (val & 0xffffffff));
+}
+
+int
+egg_buffer_get_uint64 (EggBuffer *buffer, size_t offset,
+ size_t *next_offset, uint64_t *val)
+{
+ uint32_t a, b;
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &a))
+ return 0;
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &b))
+ return 0;
+ if (val != NULL)
+ *val = ((uint64_t)a) << 32 | b;
+ if (next_offset != NULL)
+ *next_offset = offset;
+ return 1;
+}
+
+int
+egg_buffer_add_byte_array (EggBuffer *buffer, const unsigned char *val,
+ size_t len)
+{
+ if (val == NULL)
+ return egg_buffer_add_uint32 (buffer, 0xffffffff);
+ if (len >= 0x7fffffff) {
+ buffer->failures++;
+ return 0;
+ }
+ if (!egg_buffer_add_uint32 (buffer, len))
+ return 0;
+ return egg_buffer_append (buffer, val, len);
+}
+
+unsigned char*
+egg_buffer_add_byte_array_empty (EggBuffer *buffer, size_t vlen)
+{
+ if (vlen >= 0x7fffffff) {
+ buffer->failures++;
+ return NULL;
+ }
+ if (!egg_buffer_add_uint32 (buffer, vlen))
+ return NULL;
+ return egg_buffer_add_empty (buffer, vlen);
+}
+
+int
+egg_buffer_get_byte_array (EggBuffer *buffer, size_t offset,
+ size_t *next_offset, const unsigned char **val,
+ size_t *vlen)
+{
+ uint32_t len;
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &len))
+ return 0;
+ if (len == 0xffffffff) {
+ if (next_offset)
+ *next_offset = offset;
+ if (val)
+ *val = NULL;
+ if (vlen)
+ *vlen = 0;
+ return 1;
+ } else if (len >= 0x7fffffff) {
+ buffer->failures++;
+ return 0;
+ }
+
+ if (buffer->len < len || offset > buffer->len - len) {
+ buffer->failures++;
+ return 0;
+ }
+
+ if (val)
+ *val = buffer->buf + offset;
+ if (vlen)
+ *vlen = len;
+ if (next_offset)
+ *next_offset = offset + len;
+
+ return 1;
+}
+
+int
+egg_buffer_add_string (EggBuffer *buffer, const char *str)
+{
+ if (str == NULL) {
+ return egg_buffer_add_uint32 (buffer, 0xffffffff);
+ } else {
+ size_t len = strlen (str);
+ if (len >= 0x7fffffff)
+ return 0;
+ if (!egg_buffer_add_uint32 (buffer, len))
+ return 0;
+ return egg_buffer_append (buffer, (unsigned char*)str, len);
+ }
+}
+
+int
+egg_buffer_get_string (EggBuffer *buffer, size_t offset, size_t *next_offset,
+ char **str_ret, EggBufferAllocator allocator)
+{
+ uint32_t len;
+
+ if (!allocator)
+ allocator = buffer->allocator;
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &len)) {
+ return 0;
+ }
+ if (len == 0xffffffff) {
+ *next_offset = offset;
+ *str_ret = NULL;
+ return 1;
+ } else if (len >= 0x7fffffff) {
+ return 0;
+ }
+
+ if (buffer->len < len ||
+ offset > buffer->len - len) {
+ return 0;
+ }
+
+ /* Make sure no null characters in string */
+ if (memchr (buffer->buf + offset, 0, len) != NULL)
+ return 0;
+
+ /* The passed allocator may be for non-pageable memory */
+ *str_ret = (allocator) (NULL, len + 1);
+ if (!*str_ret)
+ return 0;
+ memcpy (*str_ret, buffer->buf + offset, len);
+
+ /* Always zero terminate */
+ (*str_ret)[len] = 0;
+ *next_offset = offset + len;
+
+ return 1;
+}
+
+int
+egg_buffer_add_stringv (EggBuffer *buffer, const char** strv)
+{
+ const char **v;
+ uint32_t n = 0;
+
+ if (!strv)
+ return 0;
+
+ /* Add the number of strings coming */
+ for (v = strv; *v; ++v)
+ ++n;
+ if (!egg_buffer_add_uint32 (buffer, n))
+ return 0;
+
+ /* Add the individual strings */
+ for (v = strv; *v; ++v) {
+ if (!egg_buffer_add_string (buffer, *v))
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+egg_buffer_get_stringv (EggBuffer *buffer, size_t offset, size_t *next_offset,
+ char ***strv_ret, EggBufferAllocator allocator)
+{
+ uint32_t n, i, j;
+ size_t len;
+
+ if (!allocator)
+ allocator = buffer->allocator;
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ /* First the number of environment variable lines */
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &n))
+ return 0;
+
+ /* Then that number of strings */
+ len = (n + 1) * sizeof (char*);
+ *strv_ret = (char**)(allocator) (NULL, len);
+ if (!*strv_ret)
+ return 0;
+
+ /* All null strings */
+ memset (*strv_ret, 0, len);
+
+ for (i = 0; i < n; ++i) {
+ if (!egg_buffer_get_string (buffer, offset, &offset,
+ &((*strv_ret)[i]), allocator)) {
+
+ /* Free all the strings on failure */
+ for (j = 0; j < i; ++j) {
+ if ((*strv_ret)[j])
+ (allocator) ((*strv_ret)[j], 0);
+ }
+
+ return 0;
+ }
+ }
+
+ if (next_offset != NULL)
+ *next_offset = offset;
+
+ return 1;
+}
diff --git a/egg/egg-buffer.h b/egg/egg-buffer.h
new file mode 100644
index 0000000..bd710f3
--- /dev/null
+++ b/egg/egg-buffer.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-buffer.h - Generic data buffer, used by openssh, gnome-keyring
+
+ Copyright (C) 2007, Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ .
+
+ Author: Stef Walter
+*/
+
+#ifndef EGG_BUFFER_H
+#define EGG_BUFFER_H
+
+#include
+#include
+
+/* -------------------------------------------------------------------
+ * EggBuffer
+ *
+ * IMPORTANT: This is pure vanila standard C, no glib. We need this
+ * because certain consumers of this protocol need to be built
+ * without linking in any special libraries. ie: the PKCS#11 module.
+ *
+ * Memory Allocation
+ *
+ * Callers can set their own allocator. If NULL is used then standard
+ * C library heap memory is used and failures will not be fatal. Memory
+ * failures will instead result in a zero return value or
+ * egg_buffer_has_error() returning one.
+ *
+ * If you use something like g_realloc as the allocator, then memory
+ * failures become fatal just like in a standard GTK program.
+ *
+ * Don't change the allocator manually in the EggBuffer structure. The
+ * egg_buffer_set_allocator() func will reallocate and handle things
+ * properly.
+ *
+ * Pointers into the Buffer
+ *
+ * Any write operation has the posibility of reallocating memory
+ * and invalidating any direct pointers into the buffer.
+ */
+
+/* The allocator for the EggBuffer. This follows the realloc() syntax and logic */
+typedef void* (*EggBufferAllocator) (void* p, size_t len);
+
+typedef struct _EggBuffer {
+ unsigned char *buf;
+ size_t len;
+ size_t allocated_len;
+ int failures;
+ EggBufferAllocator allocator;
+} EggBuffer;
+
+#define EGG_BUFFER_EMPTY { NULL, 0, 0, 0, NULL }
+
+int egg_buffer_init (EggBuffer *buffer, size_t reserve);
+
+int egg_buffer_init_full (EggBuffer *buffer,
+ size_t reserve,
+ EggBufferAllocator allocator);
+
+void egg_buffer_init_static (EggBuffer *buffer,
+ const unsigned char *buf,
+ size_t len);
+
+void egg_buffer_init_allocated (EggBuffer *buffer,
+ unsigned char *buf,
+ size_t len,
+ EggBufferAllocator allocator);
+
+void egg_buffer_uninit (EggBuffer *buffer);
+
+unsigned char* egg_buffer_uninit_steal (EggBuffer *buffer,
+ size_t *n_result);
+
+int egg_buffer_set_allocator (EggBuffer *buffer,
+ EggBufferAllocator allocator);
+
+void egg_buffer_reset (EggBuffer *buffer);
+
+int egg_buffer_equal (EggBuffer *b1,
+ EggBuffer *b2);
+
+int egg_buffer_reserve (EggBuffer *buffer,
+ size_t len);
+
+int egg_buffer_resize (EggBuffer *buffer,
+ size_t len);
+
+int egg_buffer_append (EggBuffer *buffer,
+ const unsigned char *val,
+ size_t len);
+
+unsigned char* egg_buffer_add_empty (EggBuffer *buffer,
+ size_t len);
+
+int egg_buffer_add_byte (EggBuffer *buffer,
+ unsigned char val);
+
+int egg_buffer_get_byte (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ unsigned char *val);
+
+void egg_buffer_encode_uint32 (unsigned char* buf,
+ uint32_t val);
+
+uint32_t egg_buffer_decode_uint32 (unsigned char* buf);
+
+int egg_buffer_add_uint32 (EggBuffer *buffer,
+ uint32_t val);
+
+int egg_buffer_set_uint32 (EggBuffer *buffer,
+ size_t offset,
+ uint32_t val);
+
+int egg_buffer_get_uint32 (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint32_t *val);
+
+void egg_buffer_encode_uint16 (unsigned char* buf,
+ uint16_t val);
+
+uint16_t egg_buffer_decode_uint16 (unsigned char* buf);
+
+int egg_buffer_add_uint16 (EggBuffer *buffer,
+ uint16_t val);
+
+int egg_buffer_set_uint16 (EggBuffer *buffer,
+ size_t offset,
+ uint16_t val);
+
+int egg_buffer_get_uint16 (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint16_t *val);
+
+int egg_buffer_add_byte_array (EggBuffer *buffer,
+ const unsigned char *val,
+ size_t len);
+
+int egg_buffer_get_byte_array (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ const unsigned char **val,
+ size_t *vlen);
+
+unsigned char* egg_buffer_add_byte_array_empty (EggBuffer *buffer,
+ size_t vlen);
+
+int egg_buffer_add_string (EggBuffer *buffer,
+ const char *str);
+
+int egg_buffer_get_string (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char **str_ret,
+ EggBufferAllocator allocator);
+
+int egg_buffer_add_stringv (EggBuffer *buffer,
+ const char** strv);
+
+int egg_buffer_get_stringv (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char ***strv_ret,
+ EggBufferAllocator allocator);
+
+int egg_buffer_add_uint64 (EggBuffer *buffer,
+ uint64_t val);
+
+int egg_buffer_get_uint64 (EggBuffer *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint64_t *val);
+
+#define egg_buffer_length(b) ((b)->len)
+
+#define egg_buffer_has_error(b) ((b)->failures > 0)
+
+#endif /* EGG_BUFFER_H */
+
diff --git a/egg/egg-unix-credentials.c b/egg/egg-unix-credentials.c
new file mode 100644
index 0000000..6366d29
--- /dev/null
+++ b/egg/egg-unix-credentials.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter
+ */
+
+#include "config.h"
+
+#include "egg-unix-credentials.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(HAVE_GETPEERUCRED)
+#include
+#endif
+
+int
+egg_unix_credentials_read (int sock, pid_t *pid, uid_t *uid)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf;
+ int ret;
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ /* Prefer CMSGCRED over LOCAL_CREDS because the former provides the
+ * remote PID. */
+#if defined(HAVE_CMSGCRED)
+ struct cmsgcred *cred;
+#else /* defined(LOCAL_CREDS) */
+ struct sockcred *cred;
+#endif
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof *cred)];
+ } cmsg;
+#endif
+
+ *pid = 0;
+ *uid = 0;
+
+ /* If LOCAL_CREDS are used in this platform, they have already been
+ * initialized by init_connection prior to sending of the credentials
+ * byte we receive below. */
+
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ memset (&cmsg, 0, sizeof (cmsg));
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof *cred);
+#endif
+
+ again:
+ ret = recvmsg (sock, &msg, 0);
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto again;
+ return -1;
+
+ } else if (ret == 0) {
+ /* Disconnected */
+ return -1;
+ }
+
+ if (buf != '\0') {
+ fprintf (stderr, "credentials byte was not nul\n");
+ return -1;
+ }
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof *cred) ||
+ cmsg.hdr.cmsg_type != SCM_CREDS) {
+ fprintf (stderr, "message from recvmsg() was not SCM_CREDS\n");
+ return -1;
+ }
+#endif
+
+ {
+#ifdef SO_PEERCRED
+#ifndef __OpenBSD__
+ struct ucred cr;
+#else
+ struct sockpeercred cr;
+#endif
+ socklen_t cr_len = sizeof (cr);
+
+ if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
+ cr_len == sizeof (cr)) {
+ *pid = cr.pid;
+ *uid = cr.uid;
+ } else {
+ fprintf (stderr, "failed to getsockopt() credentials, returned len %d/%d\n",
+ cr_len, (int) sizeof (cr));
+ return -1;
+ }
+#elif defined(HAVE_CMSGCRED)
+ cred = (struct cmsgcred *) CMSG_DATA (&cmsg.hdr);
+ *pid = cred->cmcred_pid;
+ *uid = cred->cmcred_euid;
+#elif defined(LOCAL_CREDS)
+ cred = (struct sockcred *) CMSG_DATA (&cmsg.hdr);
+ *pid = 0;
+ *uid = cred->sc_euid;
+ set_local_creds(sock, 0);
+#elif defined(HAVE_GETPEEREID) /* OpenBSD */
+ uid_t euid;
+ gid_t egid;
+ *pid = 0;
+
+ if (getpeereid (sock, &euid, &egid) == 0) {
+ *uid = euid;
+ } else {
+ fprintf (stderr, "getpeereid() failed: %s\n", strerror (errno));
+ return -1;
+ }
+#elif defined(HAVE_GETPEERUCRED)
+ ucred_t *uc = NULL;
+
+ if (getpeerucred (sock, &uc) == 0) {
+ *pid = ucred_getpid (uc);
+ *uid = ucred_geteuid (uc);
+ ucred_free (uc);
+ } else {
+ fprintf (stderr, "getpeerucred() failed: %s\n", strerror (errno));
+ return -1;
+ }
+#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
+ fprintf (stderr, "socket credentials not supported on this OS\n");
+ return -1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+egg_unix_credentials_write (int socket)
+{
+ char buf;
+ int bytes_written;
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
+ } cmsg;
+ struct iovec iov;
+ struct msghdr msg;
+#endif
+
+ buf = 0;
+
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
+ memset (&cmsg, 0, sizeof (cmsg));
+ cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
+
+again:
+
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ bytes_written = sendmsg (socket, &msg, 0);
+#else
+ bytes_written = write (socket, &buf, 1);
+#endif
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ if (bytes_written <= 0)
+ return -1;
+
+ return 0;
+}
+
+int
+egg_unix_credentials_setup (int sock)
+{
+ int retval = 0;
+#if defined(LOCAL_CREDS) && !defined(HAVE_CMSGCRED)
+ int val = 1;
+ if (setsockopt (sock, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) {
+ fprintf (stderr, "unable to set LOCAL_CREDS socket option on fd %d\n", fd);
+ retval = -1;
+ }
+#endif
+ return retval;
+}
+
+char*
+egg_unix_credentials_executable (pid_t pid)
+{
+ char *result = NULL;
+
+ /* Try and figure out the path from the pid */
+#if defined(__linux__) || defined(__FreeBSD__)
+ char path[1024];
+ char buffer[64];
+ int count;
+
+#if defined(__linux__)
+ snprintf (buffer, sizeof (buffer), "/proc/%d/exe", (int)pid);
+#elif defined(__FreeBSD__)
+ snprintf (buffer, sizeof (buffer), "/proc/%d/file", (int)pid);
+#endif
+
+ count = readlink (buffer, path, sizeof (path));
+ if (count < 0)
+ fprintf (stderr, "readlink failed for file: %s", buffer);
+ else
+ result = strndup (path, count);
+#endif
+
+ return result;
+}
diff --git a/egg/egg-unix-credentials.h b/egg/egg-unix-credentials.h
new file mode 100644
index 0000000..64b6329
--- /dev/null
+++ b/egg/egg-unix-credentials.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter
+ */
+
+#ifndef EGGUNIXCREDENTIALS_H_
+#define EGGUNIXCREDENTIALS_H_
+
+#include
+
+int egg_unix_credentials_read (int sock,
+ pid_t *pid,
+ uid_t *uid);
+
+int egg_unix_credentials_write (int sock);
+
+int egg_unix_credentials_setup (int sock);
+
+char* egg_unix_credentials_executable (pid_t pid);
+
+#endif /*EGGUNIXCREDENTIALS_H_*/
diff --git a/egg/meson.build b/egg/meson.build
index cf88390..32072f5 100644
--- a/egg/meson.build
+++ b/egg/meson.build
@@ -1,6 +1,8 @@
libegg_sources = [
'egg-hex.c',
'egg-secure-memory.c',
+ 'egg-unix-credentials.c',
+ 'egg-buffer.c',
'egg-testing.c',
]
@@ -42,6 +44,9 @@ endif
libegg = static_library('egg',
libegg_sources,
dependencies: libegg_deps,
+ c_args: [
+ '-D_GNU_SOURCE',
+ ],
include_directories: [config_h_dir, build_dir],
)