From 9cfa77f967aab91c08e590b29bcc99efaf78587d Mon Sep 17 00:00:00 2001 From: Dhanuka Warusadura Date: Wed, 15 Nov 2023 13:45:09 +0530 Subject: [PATCH] pam: port PAM module egg helper functions from gnome-keyring This change is a part of the port PAM module from gnome-keyring patch set. These changes port gnome-keyring/egg/egg-unix-credentials.c to libsecret/egg Furthermore ports gnome-keyring/egg/egg-buffer.c to libsecret/egg Signed-off-by: Dhanuka Warusadura --- egg/egg-buffer.c | 581 +++++++++++++++++++++++++++++++++++++ egg/egg-buffer.h | 196 +++++++++++++ egg/egg-unix-credentials.c | 265 +++++++++++++++++ egg/egg-unix-credentials.h | 50 ++++ egg/meson.build | 5 + 5 files changed, 1097 insertions(+) create mode 100644 egg/egg-buffer.c create mode 100644 egg/egg-buffer.h create mode 100644 egg/egg-unix-credentials.c create mode 100644 egg/egg-unix-credentials.h 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], )