mirror of
https://gitlab.gnome.org/GNOME/libsecret.git
synced 2024-12-21 20:28:52 +00:00
Merge branch 'port_pam_from_gnome_keyring' into 'master'
Port PAM module from gnome-keyring See merge request GNOME/libsecret!128
This commit is contained in:
commit
fa5ac29856
@ -1,4 +1,4 @@
|
||||
image: registry.gitlab.gnome.org/gnome/libsecret/master:v3
|
||||
image: registry.gitlab.gnome.org/gnome/libsecret/master:v5
|
||||
|
||||
stages:
|
||||
- build
|
||||
@ -78,6 +78,20 @@ fedora-static-analyzers/test:
|
||||
paths:
|
||||
- _build/meson-logs/testlog.txt
|
||||
|
||||
fedora:PAM:
|
||||
stage: build
|
||||
before_script:
|
||||
- dbus-uuidgen --ensure
|
||||
script:
|
||||
- meson _build -Dwerror=true -Dc_args=-Wno-error=deprecated-declarations -Dgtk_doc=false -Dpam=true
|
||||
- meson compile -C _build
|
||||
- eval `dbus-launch --sh-syntax`
|
||||
- meson test -C _build --print-errorlogs
|
||||
artifacts:
|
||||
when: on_failure
|
||||
paths:
|
||||
- _build/meson-logs/testlog.txt
|
||||
|
||||
fedora:coverage:
|
||||
stage: build
|
||||
before_script:
|
||||
|
@ -27,11 +27,14 @@ RUN dnf update -y \
|
||||
tpm2-tss-devel \
|
||||
vala \
|
||||
valgrind-devel \
|
||||
pam-devel \
|
||||
libpamtest-devel \
|
||||
pam_wrapper \
|
||||
&& dnf clean all
|
||||
|
||||
ARG HOST_USER_ID=5555
|
||||
ENV HOST_USER_ID ${HOST_USER_ID}
|
||||
RUN useradd -u $HOST_USER_ID -ms /bin/bash user
|
||||
RUN useradd -u $HOST_USER_ID -ms /bin/bash -p password user
|
||||
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
581
egg/egg-buffer.c
Normal file
581
egg/egg-buffer.c
Normal file
@ -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,
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: Stef Walter <stef@memberwebs.com>
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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;
|
||||
}
|
196
egg/egg-buffer.h
Normal file
196
egg/egg-buffer.h
Normal file
@ -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,
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: Stef Walter <stef@memberwebs.com>
|
||||
*/
|
||||
|
||||
#ifndef EGG_BUFFER_H
|
||||
#define EGG_BUFFER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* 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 */
|
||||
|
265
egg/egg-unix-credentials.c
Normal file
265
egg/egg-unix-credentials.c
Normal file
@ -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 <stefw@thewalter.net>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "egg-unix-credentials.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(HAVE_GETPEERUCRED)
|
||||
#include <ucred.h>
|
||||
#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;
|
||||
}
|
50
egg/egg-unix-credentials.h
Normal file
50
egg/egg-unix-credentials.h
Normal file
@ -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 <stefw@thewalter.net>
|
||||
*/
|
||||
|
||||
#ifndef EGGUNIXCREDENTIALS_H_
|
||||
#define EGGUNIXCREDENTIALS_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
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_*/
|
@ -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],
|
||||
)
|
||||
|
||||
|
@ -97,6 +97,10 @@ endif
|
||||
conf.set('WITH_DEBUG', get_option('debugging'))
|
||||
conf.set('_DEBUG', get_option('debugging'))
|
||||
conf.set('HAVE_MLOCK', meson.get_compiler('c').has_function('mlock'))
|
||||
if get_option('pam')
|
||||
conf.set_quoted('GNOME_KEYRING_DAEMON', get_option('prefix') /
|
||||
get_option('bindir') / 'gnome-keyring-daemon')
|
||||
endif
|
||||
configure_file(output: 'config.h', configuration: conf)
|
||||
|
||||
# Test environment
|
||||
@ -109,3 +113,6 @@ subdir('egg')
|
||||
subdir('libsecret')
|
||||
subdir('tool')
|
||||
subdir('docs')
|
||||
if get_option('pam')
|
||||
subdir('pam')
|
||||
endif
|
||||
|
@ -7,3 +7,4 @@ option('introspection', type: 'boolean', value: true, description: 'Create GIR f
|
||||
option('bashcompdir', type: 'string', value: '', description: 'Override default location for bash completion files')
|
||||
option('bash_completion', type: 'feature', value: 'auto', description: 'Install bash completion files')
|
||||
option('tpm2', type: 'boolean', value: false, description: 'With TPM2 Software Stack')
|
||||
option('pam', type: 'boolean', value: false, description: 'Build PAM module')
|
||||
|
38
pam/gkd-control-codes.h
Normal file
38
pam/gkd-control-codes.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* gnome-keyring
|
||||
*
|
||||
* Copyright (C) 2009 Stefan Walter
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GKD_CONTROL_CODES_H__
|
||||
#define __GKD_CONTROL_CODES_H__
|
||||
|
||||
enum {
|
||||
GKD_CONTROL_OP_INITIALIZE,
|
||||
GKD_CONTROL_OP_UNLOCK,
|
||||
GKD_CONTROL_OP_CHANGE,
|
||||
GKD_CONTROL_OP_QUIT
|
||||
};
|
||||
|
||||
enum {
|
||||
GKD_CONTROL_RESULT_OK,
|
||||
GKD_CONTROL_RESULT_DENIED,
|
||||
GKD_CONTROL_RESULT_FAILED,
|
||||
GKD_CONTROL_RESULT_NO_DAEMON
|
||||
};
|
||||
|
||||
#endif /* __GKD_CONTROL_CODES_H__ */
|
458
pam/gkr-pam-client.c
Normal file
458
pam/gkr-pam-client.c
Normal file
@ -0,0 +1,458 @@
|
||||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
||||
/* gkr-pam-client.h - Simple code for communicating with daemon
|
||||
|
||||
Copyright (C) 2007 Stef 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,
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: Stef Walter <stef@memberwebs.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gkr-pam.h"
|
||||
#include "egg/egg-unix-credentials.h"
|
||||
#include "egg/egg-buffer.h"
|
||||
#include "gkd-control-codes.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(HAVE_GETPEERUCRED)
|
||||
#include <ucred.h>
|
||||
#endif
|
||||
|
||||
#if defined(LOCAL_PEERCRED)
|
||||
#include <sys/param.h>
|
||||
#include <sys/ucred.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
check_peer_same_uid (struct passwd *pwd,
|
||||
int sock)
|
||||
{
|
||||
uid_t uid = -1;
|
||||
|
||||
/*
|
||||
* Certain OS require a message to be sent over the unix socket for the
|
||||
* otherside to get the process credentials. Most uncool.
|
||||
*
|
||||
* The normal gnome-keyring protocol accomodates this and the client
|
||||
* sends a message/byte before sending anything else. This only works
|
||||
* for the daemon verifying the client.
|
||||
*
|
||||
* This code here is used by a client to verify the daemon is running
|
||||
* as the right user. Since we cannot modify the protocol, this only
|
||||
* works on OSs that can do this credentials lookup transparently.
|
||||
*/
|
||||
|
||||
/* Linux */
|
||||
#if defined(SO_PEERCRED)
|
||||
struct ucred cr;
|
||||
socklen_t cr_len = sizeof (cr);
|
||||
|
||||
if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
|
||||
cr_len == sizeof (cr)) {
|
||||
uid = cr.uid;
|
||||
} else {
|
||||
syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, "
|
||||
"(returned len %d/%d)\n", cr_len, (int) sizeof (cr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* The BSDs */
|
||||
#elif defined(LOCAL_PEERCRED)
|
||||
uid_t gid;
|
||||
struct xucred xuc;
|
||||
socklen_t xuc_len = sizeof (xuc);
|
||||
|
||||
if (getsockopt (sock, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &xuc_len) == 0 &&
|
||||
xuc_len == sizeof (xuc)) {
|
||||
uid = xuc.cr_uid;
|
||||
} else {
|
||||
syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, "
|
||||
"(returned len %d/%d)\n", xuc_len, (int)sizeof (xuc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: Add more here */
|
||||
#else
|
||||
syslog (GKR_LOG_WARN, "Cannot verify that the process to which we are passing the login"
|
||||
" password is genuinely running as the same user login: not supported on this OS.");
|
||||
uid = pwd->pw_uid;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
if (uid != pwd->pw_uid) {
|
||||
syslog (GKR_LOG_ERR, "The gnome keyring socket is not running with the same "
|
||||
"credentials as the user login. Disconnecting.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
write_credentials_byte (int sock)
|
||||
{
|
||||
for (;;) {
|
||||
if (egg_unix_credentials_write (sock) < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
syslog (GKR_LOG_ERR, "couldn't send credentials to daemon: %s",
|
||||
strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lookup_daemon (struct passwd *pwd,
|
||||
const char *control,
|
||||
struct sockaddr_un *addr)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (strlen (control) + 1 > sizeof (addr->sun_path)) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: address is too long for unix socket path: %s",
|
||||
control);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
memset (addr, 0, sizeof (*addr));
|
||||
addr->sun_family = AF_UNIX;
|
||||
strcpy (addr->sun_path, control);
|
||||
|
||||
/* A bunch of checks to make sure nothing funny is going on */
|
||||
if (lstat (addr->sun_path, &st) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return GKD_CONTROL_RESULT_NO_DAEMON;
|
||||
|
||||
syslog (GKR_LOG_ERR, "Couldn't access gnome keyring socket: %s: %s",
|
||||
addr->sun_path, strerror (errno));
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
if (st.st_uid != pwd->pw_uid) {
|
||||
syslog (GKR_LOG_ERR, "The gnome keyring socket is not owned with the same "
|
||||
"credentials as the user login: %s", addr->sun_path);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode) || !S_ISSOCK(st.st_mode)) {
|
||||
syslog (GKR_LOG_ERR, "The gnome keyring socket is not a valid simple "
|
||||
"non-linked socket");
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
return GKD_CONTROL_RESULT_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_daemon (struct passwd *pwd,
|
||||
struct sockaddr_un *addr,
|
||||
int *out_sock)
|
||||
{
|
||||
int sock;
|
||||
|
||||
/* Now we connect */
|
||||
sock = socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
syslog (GKR_LOG_ERR, "couldn't create control socket: %s", strerror (errno));
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
/* close on exec */
|
||||
fcntl (sock, F_SETFD, 1);
|
||||
|
||||
if (connect (sock, (struct sockaddr *)addr, sizeof (*addr)) < 0) {
|
||||
if (errno == ECONNREFUSED) {
|
||||
close (sock);
|
||||
return GKD_CONTROL_RESULT_NO_DAEMON;
|
||||
}
|
||||
syslog (GKR_LOG_ERR, "couldn't connect to gnome-keyring-daemon socket at: %s: %s",
|
||||
addr->sun_path, strerror (errno));
|
||||
close (sock);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
/* Verify the server is running as the right user */
|
||||
|
||||
if (check_peer_same_uid (pwd, sock) <= 0) {
|
||||
close (sock);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
/* This lets the server verify us */
|
||||
|
||||
if (write_credentials_byte (sock) < 0) {
|
||||
close (sock);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
*out_sock = sock;
|
||||
return GKD_CONTROL_RESULT_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
write_part (int fd, const unsigned char *data, int len, int *res)
|
||||
{
|
||||
assert (res);
|
||||
|
||||
/* Already an error present */
|
||||
if (*res != GKD_CONTROL_RESULT_OK)
|
||||
return;
|
||||
|
||||
assert (data);
|
||||
|
||||
while (len > 0) {
|
||||
int r = write (fd, data, len);
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
syslog (GKR_LOG_ERR, "couldn't send data to gnome-keyring-daemon: %s",
|
||||
strerror (errno));
|
||||
*res = GKD_CONTROL_RESULT_FAILED;
|
||||
return;
|
||||
}
|
||||
data += r;
|
||||
len -= r;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
read_part (int fd,
|
||||
unsigned char *data,
|
||||
int len,
|
||||
int disconnect_ok)
|
||||
{
|
||||
int r, all;
|
||||
|
||||
all = len;
|
||||
while (len > 0) {
|
||||
r = read (fd, data, len);
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
if (errno == ECONNRESET && disconnect_ok)
|
||||
return 0;
|
||||
syslog (GKR_LOG_ERR, "couldn't read data from gnome-keyring-daemon: %s",
|
||||
strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
if (r == 0) {
|
||||
if (disconnect_ok)
|
||||
return 0;
|
||||
syslog (GKR_LOG_ERR, "couldn't read data from gnome-keyring-daemon: %s",
|
||||
"unexpected end of data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
data += r;
|
||||
len -= r;
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
static int
|
||||
keyring_daemon_op (struct passwd *pwd,
|
||||
struct sockaddr_un *addr,
|
||||
int op,
|
||||
int argc,
|
||||
const char *argv[])
|
||||
{
|
||||
int ret = GKD_CONTROL_RESULT_OK;
|
||||
unsigned char buf[4];
|
||||
int want_disconnect;
|
||||
int i, sock = -1;
|
||||
uint oplen, l;
|
||||
|
||||
assert (addr);
|
||||
|
||||
/*
|
||||
* We only support operations with zero or more strings
|
||||
* and an empty (only result code) return.
|
||||
*/
|
||||
|
||||
assert (op == GKD_CONTROL_OP_CHANGE ||
|
||||
op == GKD_CONTROL_OP_UNLOCK ||
|
||||
op == GKD_CONTROL_OP_QUIT);
|
||||
|
||||
ret = connect_daemon (pwd, addr, &sock);
|
||||
if (ret != GKD_CONTROL_RESULT_OK)
|
||||
goto done;
|
||||
|
||||
/* Calculate the packet length */
|
||||
oplen = 8; /* The packet size, and op code */
|
||||
for (i = 0; i < argc; ++i)
|
||||
oplen += 4 + strlen (argv[i]);
|
||||
|
||||
/* Write out the length, and op */
|
||||
egg_buffer_encode_uint32 (buf, oplen);
|
||||
write_part (sock, buf, 4, &ret);
|
||||
egg_buffer_encode_uint32 (buf, op);
|
||||
write_part (sock, buf, 4, &ret);
|
||||
|
||||
/* And now the arguments */
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (argv[i] == NULL)
|
||||
l = 0x7FFFFFFF;
|
||||
else
|
||||
l = strlen (argv[i]);
|
||||
egg_buffer_encode_uint32 (buf, l);
|
||||
write_part (sock, buf, 4, &ret);
|
||||
if (argv[i] != NULL)
|
||||
write_part (sock, (unsigned char*)argv[i], l, &ret);
|
||||
}
|
||||
|
||||
if (ret != GKD_CONTROL_RESULT_OK)
|
||||
goto done;
|
||||
/*
|
||||
* If we're asking the daemon to quit, then we expect
|
||||
* disconnects after we send the initial request
|
||||
*/
|
||||
want_disconnect = (op == GKD_CONTROL_OP_QUIT);
|
||||
|
||||
/* Read the response length */
|
||||
if (read_part (sock, buf, 4, want_disconnect) != 4) {
|
||||
ret = GKD_CONTROL_RESULT_FAILED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We only support simple responses */
|
||||
l = egg_buffer_decode_uint32 (buf);
|
||||
if (l != 8) {
|
||||
syslog (GKR_LOG_ERR, "invalid length response from gnome-keyring-daemon: %d", l);
|
||||
ret = GKD_CONTROL_RESULT_FAILED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (read_part (sock, buf, 4, want_disconnect) != 4) {
|
||||
ret = GKD_CONTROL_RESULT_FAILED;
|
||||
goto done;
|
||||
}
|
||||
ret = egg_buffer_decode_uint32 (buf);
|
||||
|
||||
/*
|
||||
* If we asked the daemon to quit, wait for it to disconnect
|
||||
* by waiting until the socket disconnects from the other end.
|
||||
*/
|
||||
if (want_disconnect) {
|
||||
while (read (sock, buf, 4) > 0);
|
||||
}
|
||||
|
||||
done:
|
||||
if (sock >= 0)
|
||||
close (sock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
gkr_pam_client_run_operation (struct passwd *pwd, const char *control,
|
||||
int op, int argc, const char* argv[])
|
||||
{
|
||||
struct sigaction ignpipe, oldpipe, defchld, oldchld;
|
||||
struct sockaddr_un addr;
|
||||
int res;
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
/* Make dumb signals go away */
|
||||
memset (&ignpipe, 0, sizeof (ignpipe));
|
||||
memset (&oldpipe, 0, sizeof (oldpipe));
|
||||
ignpipe.sa_handler = SIG_IGN;
|
||||
sigaction (SIGPIPE, &ignpipe, &oldpipe);
|
||||
|
||||
memset (&defchld, 0, sizeof (defchld));
|
||||
memset (&oldchld, 0, sizeof (oldchld));
|
||||
defchld.sa_handler = SIG_DFL;
|
||||
sigaction (SIGCHLD, &defchld, &oldchld);
|
||||
|
||||
res = lookup_daemon (pwd, control, &addr);
|
||||
if (res != GKD_CONTROL_RESULT_OK)
|
||||
goto out;
|
||||
|
||||
if (pwd->pw_uid == getuid () && pwd->pw_gid == getgid () &&
|
||||
pwd->pw_uid == geteuid () && pwd->pw_gid == getegid ()) {
|
||||
|
||||
/* Already running as the right user, simple */
|
||||
res = keyring_daemon_op (pwd, &addr, op, argc, argv);
|
||||
|
||||
} else {
|
||||
|
||||
/* Otherwise run a child process to do the dirty work */
|
||||
switch (pid = fork ()) {
|
||||
case -1:
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't fork: %s",
|
||||
strerror (errno));
|
||||
res = GKD_CONTROL_RESULT_FAILED;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* Setup process credentials */
|
||||
if (setgid (pwd->pw_gid) < 0 || setuid (pwd->pw_uid) < 0 ||
|
||||
setegid (pwd->pw_gid) < 0 || seteuid (pwd->pw_uid) < 0) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't switch to user: %s: %s",
|
||||
pwd->pw_name, strerror (errno));
|
||||
exit (GKD_CONTROL_RESULT_FAILED);
|
||||
}
|
||||
|
||||
res = keyring_daemon_op (pwd, &addr, op, argc, argv);
|
||||
exit (res);
|
||||
return 0; /* Never reached */
|
||||
|
||||
default:
|
||||
/* wait for child process */
|
||||
if (wait (&status) != pid) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't wait on child process: %s",
|
||||
strerror (errno));
|
||||
res = GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
res = WEXITSTATUS (status);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
out:
|
||||
sigaction (SIGCHLD, &oldchld, NULL);
|
||||
sigaction (SIGPIPE, &oldpipe, NULL);
|
||||
|
||||
return res;
|
||||
}
|
616
pam/gkr-pam-module.c
Normal file
616
pam/gkr-pam-module.c
Normal file
@ -0,0 +1,616 @@
|
||||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
||||
/* gkr-pam-module.h - A PAM module for unlocking the keyring
|
||||
|
||||
Copyright (C) 2007 Stef 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,
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: Stef Walter <stef@memberwebs.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Inspired by pam_keyring:
|
||||
* W. Michael Petullo <mike@flyn.org>
|
||||
* Jonathan Nettleton <jon.nettleton@gmail.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gkr-pam.h"
|
||||
#include "gkd-control-codes.h"
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_ext.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(ENABLE_NLS) && defined(__linux__)
|
||||
#include <libintl.h>
|
||||
#define gkr_pam_gettext(msgid) dgettext ("Linux-PAM", msgid)
|
||||
#else
|
||||
#define gkr_pam_gettext(msgid) (msgid)
|
||||
#endif /* ENABLE_NLS */
|
||||
|
||||
enum {
|
||||
ARG_AUTO_START = 1 << 0,
|
||||
ARG_IGNORE_SERVICE = 1 << 1,
|
||||
ARG_USE_AUTHTOK = 1 << 2
|
||||
};
|
||||
|
||||
#define ENV_CONTROL "GNOME_KEYRING_CONTROL"
|
||||
|
||||
#define MAX_CONTROL_SIZE (sizeof(((struct sockaddr_un *)0)->sun_path))
|
||||
|
||||
/* read & write ends of a pipe */
|
||||
#define READ_END 0
|
||||
#define WRITE_END 1
|
||||
|
||||
/* pre-set file descriptors */
|
||||
#define STDIN 0
|
||||
#define STDOUT 1
|
||||
#define STDERR 2
|
||||
|
||||
/* Linux/BSD compatibility */
|
||||
#ifndef PAM_AUTHTOK_RECOVER_ERR
|
||||
#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
|
||||
#endif
|
||||
|
||||
#ifndef PAM_EXTERN
|
||||
#ifdef PAM_STATIC
|
||||
#define PAM_EXTERN static
|
||||
#else
|
||||
#define PAM_EXTERN extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* HELPERS
|
||||
*/
|
||||
|
||||
static void
|
||||
free_password (char *password)
|
||||
{
|
||||
volatile char *vp;
|
||||
size_t len;
|
||||
|
||||
if (!password)
|
||||
return;
|
||||
|
||||
/* Defeats some optimizations */
|
||||
len = strlen (password);
|
||||
memset (password, 0xAA, len);
|
||||
memset (password, 0xBB, len);
|
||||
|
||||
/* Defeats others */
|
||||
vp = (volatile char*)password;
|
||||
while (*vp)
|
||||
*(vp++) = 0xAA;
|
||||
|
||||
free (password);
|
||||
}
|
||||
|
||||
/* check for list match. */
|
||||
static int
|
||||
evaluate_inlist (const char *needle, const char *haystack)
|
||||
{
|
||||
const char *item;
|
||||
const char *remaining;
|
||||
|
||||
if (!needle)
|
||||
return 0;
|
||||
|
||||
remaining = haystack;
|
||||
|
||||
for (;;) {
|
||||
item = strstr (remaining, needle);
|
||||
if (item == NULL)
|
||||
break;
|
||||
|
||||
/* is it really the start of an item in the list? */
|
||||
if (item == haystack || *(item - 1) == ',') {
|
||||
item += strlen (needle);
|
||||
/* is item really needle? */
|
||||
if (*item == '\0' || *item == ',')
|
||||
return 1;
|
||||
}
|
||||
|
||||
remaining = strchr (item, ',');
|
||||
if (remaining == NULL)
|
||||
break;
|
||||
|
||||
/* skip ',' */
|
||||
++remaining;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* DAEMON MANAGEMENT
|
||||
*/
|
||||
|
||||
static const char*
|
||||
get_any_env (pam_handle_t *ph, const char *name)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
assert (name);
|
||||
|
||||
/* We only return non-empty variables */
|
||||
|
||||
/*
|
||||
* Some PAMs decide to strdup the return value, not sure
|
||||
* how we can detect this.
|
||||
*/
|
||||
env = pam_getenv (ph, name);
|
||||
if (env && env[0])
|
||||
return env;
|
||||
|
||||
env = getenv (name);
|
||||
if (env && env[0])
|
||||
return env;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_free_password (pam_handle_t *ph, void *data, int pam_end_status)
|
||||
{
|
||||
free_password (data);
|
||||
}
|
||||
|
||||
/* control must be at least MAX_CONTROL_SIZE */
|
||||
static int
|
||||
get_control_file (pam_handle_t *ph, char *control)
|
||||
{
|
||||
const char *control_root;
|
||||
const char *suffix;
|
||||
|
||||
control_root = get_any_env (ph, ENV_CONTROL);
|
||||
if (control_root == NULL) {
|
||||
control_root = get_any_env (ph, "XDG_RUNTIME_DIR");
|
||||
if (control_root == NULL)
|
||||
return GKD_CONTROL_RESULT_NO_DAEMON;
|
||||
suffix = "/keyring/control";
|
||||
} else {
|
||||
suffix = "/control";
|
||||
}
|
||||
|
||||
if (strlen (control_root) + strlen (suffix) + 1 > MAX_CONTROL_SIZE) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: address is too long for unix socket path: %s/%s",
|
||||
control, suffix);
|
||||
return GKD_CONTROL_RESULT_FAILED;
|
||||
}
|
||||
|
||||
strcpy (control, control_root);
|
||||
strcat (control, suffix);
|
||||
|
||||
return GKD_CONTROL_RESULT_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
unlock_keyring (pam_handle_t *ph,
|
||||
struct passwd *pwd,
|
||||
const char *password)
|
||||
{
|
||||
char control[MAX_CONTROL_SIZE];
|
||||
int res;
|
||||
const char *argv[2];
|
||||
|
||||
assert (pwd);
|
||||
|
||||
res = get_control_file(ph, control);
|
||||
if (res != GKD_CONTROL_RESULT_OK) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: unable to locate daemon control file");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
argv[0] = password;
|
||||
|
||||
res = gkr_pam_client_run_operation (pwd, control, GKD_CONTROL_OP_UNLOCK,
|
||||
(argv[0] == NULL) ? 0 : 1, argv);
|
||||
/* An error unlocking */
|
||||
if (res == GKD_CONTROL_RESULT_NO_DAEMON) {
|
||||
return PAM_SERVICE_ERR;
|
||||
} else if (res == GKD_CONTROL_RESULT_DENIED) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: the password for the login keyring was invalid.");
|
||||
return PAM_SERVICE_ERR;
|
||||
} else if (res != GKD_CONTROL_RESULT_OK) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't unlock the login keyring.");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
syslog (GKR_LOG_INFO, "gkr-pam: unlocked login keyring");
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
change_keyring_password (pam_handle_t *ph,
|
||||
struct passwd *pwd,
|
||||
const char *password,
|
||||
const char *original)
|
||||
{
|
||||
char control[MAX_CONTROL_SIZE];
|
||||
const char *argv[3];
|
||||
int res;
|
||||
|
||||
assert (pwd);
|
||||
assert (password);
|
||||
assert (original);
|
||||
|
||||
res = get_control_file(ph, control);
|
||||
if (res != GKD_CONTROL_RESULT_OK) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: unable to locate daemon control file");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
argv[0] = original;
|
||||
argv[1] = password;
|
||||
|
||||
res = gkr_pam_client_run_operation (pwd, control, GKD_CONTROL_OP_CHANGE, 2, argv);
|
||||
|
||||
if (res == GKD_CONTROL_RESULT_NO_DAEMON) {
|
||||
return PAM_SERVICE_ERR;
|
||||
/* No keyring, not an error. Will be created at initial authenticate. */
|
||||
} else if (res == GKD_CONTROL_RESULT_DENIED) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't change password for the login keyring: the passwords didn't match.");
|
||||
return PAM_SERVICE_ERR;
|
||||
} else if (res != GKD_CONTROL_RESULT_OK) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't change password for the login keyring.");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
syslog (GKR_LOG_NOTICE, "gkr-pam: changed password for login keyring");
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* PAM STUFF
|
||||
*/
|
||||
|
||||
static int
|
||||
prompt_password (pam_handle_t *ph)
|
||||
{
|
||||
const struct pam_conv *conv;
|
||||
struct pam_message msg;
|
||||
struct pam_response *resp;
|
||||
const struct pam_message *msgs[1];
|
||||
const void *item;
|
||||
char *password;
|
||||
int ret;
|
||||
|
||||
/* Get the conversation function */
|
||||
ret = pam_get_item (ph, PAM_CONV, &item);
|
||||
if (ret != PAM_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/* Setup a message */
|
||||
memset (&msg, 0, sizeof (msg));
|
||||
memset (&resp, 0, sizeof (resp));
|
||||
msg.msg_style = PAM_PROMPT_ECHO_OFF;
|
||||
msg.msg = gkr_pam_gettext ("Password: ");
|
||||
msgs[0] = &msg;
|
||||
|
||||
/* Call away */
|
||||
conv = (const struct pam_conv*)item;
|
||||
ret = (conv->conv) (1, msgs, &resp, conv->appdata_ptr);
|
||||
if (ret != PAM_SUCCESS)
|
||||
return ret;
|
||||
|
||||
password = resp[0].resp;
|
||||
free (resp);
|
||||
|
||||
if (password == NULL)
|
||||
return PAM_CONV_ERR;
|
||||
|
||||
/* Store it away for later use */
|
||||
ret = pam_set_item (ph, PAM_AUTHTOK, password);
|
||||
free_password (password);
|
||||
|
||||
if (ret == PAM_SUCCESS)
|
||||
ret = pam_get_item (ph, PAM_AUTHTOK, &item);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint
|
||||
parse_args (pam_handle_t *ph, int argc, const char **argv)
|
||||
{
|
||||
uint args = 0;
|
||||
const void *svc;
|
||||
int only_if_len;
|
||||
int i;
|
||||
|
||||
svc = NULL;
|
||||
if (pam_get_item (ph, PAM_SERVICE, &svc) != PAM_SUCCESS)
|
||||
svc = NULL;
|
||||
|
||||
only_if_len = strlen ("only_if=");
|
||||
|
||||
/* Parse the arguments */
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (strcmp (argv[i], "auto_start") == 0) {
|
||||
args |= ARG_AUTO_START;
|
||||
|
||||
} else if (strncmp (argv[i], "only_if=", only_if_len) == 0) {
|
||||
const char *value = argv[i] + only_if_len;
|
||||
if (!evaluate_inlist (svc, value))
|
||||
args |= ARG_IGNORE_SERVICE;
|
||||
|
||||
} else if (strcmp (argv[i], "use_authtok") == 0) {
|
||||
args |= ARG_USE_AUTHTOK;
|
||||
|
||||
} else {
|
||||
syslog (GKR_LOG_WARN, "gkr-pam: invalid option: %s",
|
||||
argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
static int
|
||||
stash_password_for_session (pam_handle_t *ph,
|
||||
const char *password)
|
||||
{
|
||||
if (pam_set_data (ph, "gkr_system_authtok", strdup (password),
|
||||
cleanup_free_password) != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: error stashing password for session");
|
||||
return PAM_AUTHTOK_RECOVER_ERR;
|
||||
}
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_authenticate (pam_handle_t *ph, int unused, int argc, const char **argv)
|
||||
{
|
||||
struct passwd *pwd;
|
||||
const char *user, *password;
|
||||
uint args;
|
||||
int ret;
|
||||
|
||||
args = parse_args (ph, argc, argv);
|
||||
|
||||
if (args & ARG_IGNORE_SERVICE)
|
||||
return PAM_SUCCESS;
|
||||
|
||||
/* Figure out and/or prompt for the user name */
|
||||
ret = pam_get_user (ph, &user, NULL);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the user name: %s",
|
||||
pam_strerror (ph, ret));
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
pwd = getpwnam (user);
|
||||
if (!pwd) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: error looking up user information");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
/* Look up the password */
|
||||
ret = pam_get_authtok (ph, PAM_AUTHTOK, &password, NULL);
|
||||
if (ret != PAM_SUCCESS || password == NULL) {
|
||||
if (ret == PAM_SUCCESS)
|
||||
syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user");
|
||||
else
|
||||
syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user: %s",
|
||||
pam_strerror (ph, ret));
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
ret = unlock_keyring (ph, pwd, password);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
ret = stash_password_for_session (ph, password);
|
||||
syslog (GKR_LOG_INFO, "gkr-pam: stashed password to try later in open session");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_open_session (pam_handle_t *ph, int flags, int argc, const char **argv)
|
||||
{
|
||||
const char *user = NULL, *password = NULL;
|
||||
struct passwd *pwd;
|
||||
int ret;
|
||||
uint args;
|
||||
|
||||
args = parse_args (ph, argc, argv);
|
||||
|
||||
if (args & ARG_IGNORE_SERVICE)
|
||||
return PAM_SUCCESS;
|
||||
|
||||
/* Figure out the user name */
|
||||
ret = pam_get_user (ph, &user, NULL);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the user name: %s",
|
||||
pam_strerror (ph, ret));
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
pwd = getpwnam (user);
|
||||
if (!pwd) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: error looking up user information for: %s", user);
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
/* Get the stored authtok here */
|
||||
if (pam_get_data (ph, "gkr_system_authtok", (const void**)&password) != PAM_SUCCESS) {
|
||||
/*
|
||||
* No password, no worries, maybe this (PAM using) application
|
||||
* didn't do authentication, or is hopeless and wants to call
|
||||
* different PAM callbacks from different processes.
|
||||
*
|
||||
* No use complaining
|
||||
*/
|
||||
password = NULL;
|
||||
}
|
||||
|
||||
if (args & ARG_AUTO_START || password) {
|
||||
ret = unlock_keyring (ph, pwd, password);
|
||||
if (ret != PAM_SUCCESS)
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
/* Destroy the stored authtok once it has been used */
|
||||
if (password && pam_set_data (ph, "gkr_system_authtok", NULL, NULL) != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: error destroying the password");
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_setcred (pam_handle_t * ph, int flags, int argc, const char **argv)
|
||||
{
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
pam_chauthtok_preliminary (pam_handle_t *ph, struct passwd *pwd)
|
||||
{
|
||||
/*
|
||||
* If a super-user is changing a user's password then pam_unix.so
|
||||
* doesn't prompt for the user's current password, which means we
|
||||
* won't have access to that password to change the keyring password.
|
||||
*
|
||||
* So we could prompt for the current user's password except that
|
||||
* most software is broken in this regard, and doesn't use the
|
||||
* prompts properly.
|
||||
*
|
||||
* In addition how would we verify the user's password? We could
|
||||
* verify it against the Gnome Keyring, but if it is mismatched
|
||||
* from teh UNIX password then that would be super confusing.
|
||||
*
|
||||
* So we opt, just to send NULL along with the change password
|
||||
* request and have the user type in their current GNOME Keyring
|
||||
* password at an explanatory prompt.
|
||||
*/
|
||||
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
|
||||
static int
|
||||
pam_chauthtok_update (pam_handle_t *ph, struct passwd *pwd, uint args)
|
||||
{
|
||||
const char *password, *original;
|
||||
int ret;
|
||||
|
||||
ret = pam_get_authtok (ph, PAM_AUTHTOK, &password, NULL);
|
||||
if (ret != PAM_SUCCESS)
|
||||
password = NULL;
|
||||
|
||||
ret = pam_get_authtok (ph, PAM_OLDAUTHTOK, &original, NULL);
|
||||
if (ret != PAM_SUCCESS || original == NULL) {
|
||||
syslog (GKR_LOG_WARN, "gkr-pam: couldn't update the login keyring password: %s",
|
||||
"no old password was entered");
|
||||
if (password)
|
||||
stash_password_for_session (ph, password);
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
|
||||
if (password == NULL) {
|
||||
/* No password was set, and we can't prompt for it */
|
||||
if (args & ARG_USE_AUTHTOK) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: no password set, and use_authtok was specified");
|
||||
return PAM_AUTHTOK_RECOVER_ERR;
|
||||
}
|
||||
|
||||
/* No password was entered, prompt for it */
|
||||
ret = prompt_password (ph);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the password from user: %s",
|
||||
pam_strerror (ph, ret));
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
ret = pam_get_authtok (ph, PAM_AUTHTOK, &password, NULL);
|
||||
if (ret != PAM_SUCCESS || password == NULL) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the password from user: %s",
|
||||
ret == PAM_SUCCESS ? "password was null" : pam_strerror (ph, ret));
|
||||
return PAM_AUTHTOK_RECOVER_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
ret = change_keyring_password (ph, pwd, password, original);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
/* Store the password for our session handler */
|
||||
stash_password_for_session (ph, password);
|
||||
syslog (GKR_LOG_INFO, "gkr-pam: stashed password to try later in open session");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_close_session (pam_handle_t *ph, int flags, int argc, const char **argv)
|
||||
{
|
||||
/* Nothing to do, but we have to have this function exported */
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_chauthtok (pam_handle_t *ph, int flags, int argc, const char **argv)
|
||||
{
|
||||
const char *user;
|
||||
struct passwd *pwd;
|
||||
uint args;
|
||||
int ret;
|
||||
|
||||
args = parse_args (ph, argc, argv);
|
||||
|
||||
if (args & ARG_IGNORE_SERVICE)
|
||||
return PAM_SUCCESS;
|
||||
|
||||
/* Figure out and/or prompt for the user name */
|
||||
ret = pam_get_user (ph, &user, NULL);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the user name: %s",
|
||||
pam_strerror (ph, ret));
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
pwd = getpwnam (user);
|
||||
if (!pwd) {
|
||||
syslog (GKR_LOG_ERR, "gkr-pam: error looking up user information for: %s", user);
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
if (flags & PAM_PRELIM_CHECK)
|
||||
return pam_chauthtok_preliminary (ph, pwd);
|
||||
else if (flags & PAM_UPDATE_AUTHTOK)
|
||||
return pam_chauthtok_update (ph, pwd, args);
|
||||
else
|
||||
return PAM_IGNORE;
|
||||
}
|
40
pam/gkr-pam.h
Normal file
40
pam/gkr-pam.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
||||
/* gkr-pam.h - Common PAM definitions
|
||||
|
||||
Copyright (C) 2007 Stef 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,
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: Stef Walter <stef@memberwebs.com>
|
||||
*/
|
||||
|
||||
#ifndef GKRPAM_H_
|
||||
#define GKRPAM_H_
|
||||
|
||||
#include <pwd.h>
|
||||
|
||||
#ifndef LOG_AUTHPRIV
|
||||
#define LOG_AUTHPRIV LOG_AUTH
|
||||
#endif
|
||||
|
||||
#define GKR_LOG_ERR (LOG_ERR | LOG_AUTHPRIV)
|
||||
#define GKR_LOG_WARN (LOG_WARNING | LOG_AUTHPRIV)
|
||||
#define GKR_LOG_NOTICE (LOG_NOTICE | LOG_AUTHPRIV)
|
||||
#define GKR_LOG_INFO (LOG_INFO | LOG_AUTHPRIV)
|
||||
|
||||
int gkr_pam_client_run_operation (struct passwd *pwd, const char *socket,
|
||||
int op, int argc, const char* argv[]);
|
||||
|
||||
#endif /*GKRPAM_H_*/
|
45
pam/meson.build
Normal file
45
pam/meson.build
Normal file
@ -0,0 +1,45 @@
|
||||
# pam source
|
||||
pam = dependency('pam', required: true)
|
||||
|
||||
pam_gnome_keyring = shared_library('pam_gnome_keyring',
|
||||
sources: [
|
||||
'gkr-pam-module.c',
|
||||
'gkr-pam-client.c',
|
||||
],
|
||||
dependencies: [
|
||||
pam,
|
||||
glib_deps,
|
||||
],
|
||||
include_directories: config_h_dir,
|
||||
link_with: libegg,
|
||||
c_args: [
|
||||
'-D_GNU_SOURCE',
|
||||
],
|
||||
name_prefix: '',
|
||||
)
|
||||
|
||||
# pam tests
|
||||
pam_wrapper = dependency('pam_wrapper', required: true)
|
||||
libpamtest = dependency('libpamtest', required: true)
|
||||
|
||||
subdir('servicedir')
|
||||
|
||||
test_bin = executable('pam_test',
|
||||
sources: [
|
||||
'test-pam.c',
|
||||
],
|
||||
dependencies: [
|
||||
libpamtest,
|
||||
glib_deps,
|
||||
],
|
||||
)
|
||||
|
||||
test('pam-test',
|
||||
test_bin,
|
||||
env: {
|
||||
'LD_PRELOAD': 'libpam_wrapper.so',
|
||||
'PAM_WRAPPER': '1',
|
||||
'PAM_WRAPPER_DEBUGLEVEL': '5',
|
||||
'PAM_WRAPPER_SERVICE_DIR': meson.current_build_dir() + '/servicedir',
|
||||
},
|
||||
)
|
11
pam/servicedir/meson.build
Normal file
11
pam/servicedir/meson.build
Normal file
@ -0,0 +1,11 @@
|
||||
custom_target('pam-test-service',
|
||||
command: 'true',
|
||||
output: 'null',
|
||||
depend_files: configure_file(
|
||||
input: 'pam-test-service.in',
|
||||
output: 'pam-test-service',
|
||||
configuration: configuration_data({
|
||||
'KEYRING_PAM': pam_gnome_keyring.full_path(),
|
||||
}),
|
||||
),
|
||||
)
|
3
pam/servicedir/pam-test-service.in
Normal file
3
pam/servicedir/pam-test-service.in
Normal file
@ -0,0 +1,3 @@
|
||||
auth required @KEYRING_PAM@
|
||||
password required @KEYRING_PAM@
|
||||
session required @KEYRING_PAM@
|
173
pam/test-pam.c
Normal file
173
pam/test-pam.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* libsecret
|
||||
*
|
||||
* Copyright (C) 2023 GNOME Foundation Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received copies of the GNU General Public License and
|
||||
* the GNU Lesser General Public License along with this program. If
|
||||
* not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Author: Dhanuka Warusadura
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
|
||||
#include <libpamtest.h>
|
||||
|
||||
#define SERVICE "pam-test-service"
|
||||
#define BUFFER_SIZE 100
|
||||
|
||||
typedef struct {
|
||||
gchar *control_path;
|
||||
GSocketAddress *address;
|
||||
GSocketService *service;
|
||||
GThread *pam_test;
|
||||
gboolean success;
|
||||
} Test;
|
||||
|
||||
static gchar dir_path[] = "/tmp/pam_test_XXXXXX";
|
||||
|
||||
static gboolean
|
||||
is_bytes_exchanged (GSocketService *service,
|
||||
GSocketConnection *connection,
|
||||
GObject *source_object,
|
||||
gpointer data)
|
||||
{
|
||||
Test *test = data;
|
||||
GInputStream *input;
|
||||
GError *error = NULL;
|
||||
char buffer[BUFFER_SIZE + 1];
|
||||
|
||||
g_printf ("Incoming signal detected\n");
|
||||
|
||||
if (g_socket_service_is_active (service))
|
||||
test->success = TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
input = g_io_stream_get_input_stream (G_IO_STREAM(connection));
|
||||
|
||||
if (g_input_stream_read (input,
|
||||
buffer,
|
||||
BUFFER_SIZE,
|
||||
NULL,
|
||||
&error) > 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
teardown (Test *test)
|
||||
{
|
||||
g_assert_true (g_socket_service_is_active (test->service));
|
||||
g_thread_join (test->pam_test);
|
||||
g_socket_service_stop (test->service);
|
||||
g_assert_false (g_socket_service_is_active (test->service));
|
||||
|
||||
g_object_unref (test->address);
|
||||
g_unlink (test->control_path);
|
||||
g_free (test->control_path);
|
||||
g_free (test);
|
||||
g_rmdir (dir_path);
|
||||
|
||||
g_printf ("Teardown completed\n");
|
||||
}
|
||||
|
||||
static void *
|
||||
pam_auth_tests (gpointer data)
|
||||
{
|
||||
Test *test = data;
|
||||
enum pamtest_err ret;
|
||||
|
||||
g_printf ("Executing PAM auth tests\n");
|
||||
|
||||
const char *auth_tokens[] = {
|
||||
"password",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct pamtest_conv_data conv_data = {
|
||||
.in_echo_off = auth_tokens
|
||||
};
|
||||
|
||||
struct pam_testcase tests[] = {
|
||||
pam_test (PAMTEST_AUTHENTICATE, PAM_SUCCESS)
|
||||
};
|
||||
|
||||
g_assert_true (g_socket_service_is_active (test->service));
|
||||
ret = run_pamtest (SERVICE, g_get_user_name (), &conv_data, tests, NULL);
|
||||
g_assert_cmpint (ret, ==, PAMTEST_ERR_OK);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_service (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
Test *test;
|
||||
|
||||
g_printf ("Starting mock service\n");
|
||||
|
||||
test = g_malloc (sizeof (Test));
|
||||
test->success = FALSE;
|
||||
|
||||
g_mkdtemp (dir_path);
|
||||
g_setenv ("GNOME_KEYRING_CONTROL", dir_path, TRUE);
|
||||
test->control_path = g_strconcat (dir_path, "/control", NULL);
|
||||
|
||||
test->address = g_unix_socket_address_new (test->control_path);
|
||||
test->service = g_socket_service_new ();
|
||||
g_socket_service_stop (test->service);
|
||||
g_assert_false (g_socket_service_is_active (test->service));
|
||||
|
||||
g_signal_connect (test->service,
|
||||
"incoming",
|
||||
G_CALLBACK (is_bytes_exchanged),
|
||||
test);
|
||||
|
||||
g_socket_listener_add_address (G_SOCKET_LISTENER (test->service),
|
||||
test->address,
|
||||
G_SOCKET_TYPE_STREAM,
|
||||
G_SOCKET_PROTOCOL_DEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_socket_service_start (test->service);
|
||||
g_assert_true (g_socket_service_is_active (test->service));
|
||||
|
||||
test->pam_test = g_thread_new ("pam_test", pam_auth_tests, test);
|
||||
|
||||
do
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
while (!test->success);
|
||||
|
||||
teardown (test);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/pam/test_pam_authtok", mock_service);
|
||||
|
||||
return g_test_run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user