blob: 21d0ab0e5da94400b632a1276baad0ea7d31bcc4 [file] [log] [blame]
/* keyutils.c: key utility library
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* 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 of the License, or (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <errno.h>
#include <asm/unistd.h>
#include "keyutils.h"
#ifdef NO_GLIBC_KEYERR
static int error_inited;
static void (*libc_perror)(const char *msg);
static char *(*libc_strerror_r)(int errnum, char *buf, size_t n);
//static int (*libc_xpg_strerror_r)(int errnum, char *buf, size_t n);
#define RTLD_NEXT ((void *) -1L)
#endif
#define __weak __attribute__((weak))
key_serial_t __weak add_key(const char *type,
const char *description,
const void *payload,
size_t plen,
key_serial_t ringid)
{
return syscall(__NR_add_key,
type, description, payload, plen, ringid);
}
key_serial_t __weak request_key(const char *type,
const char *description,
const char * callout_info,
key_serial_t destringid)
{
return syscall(__NR_request_key,
type, description, callout_info, destringid);
}
static inline long __keyctl(int cmd,
unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
unsigned long arg5)
{
return syscall(__NR_keyctl,
cmd, arg2, arg3, arg4, arg5);
}
long __weak keyctl(int cmd, ...)
{
va_list va;
unsigned long arg2, arg3, arg4, arg5;
va_start(va, cmd);
arg2 = va_arg(va, unsigned long);
arg3 = va_arg(va, unsigned long);
arg4 = va_arg(va, unsigned long);
arg5 = va_arg(va, unsigned long);
va_end(va);
return __keyctl(cmd, arg2, arg3, arg4, arg5);
}
key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create)
{
return keyctl(KEYCTL_GET_KEYRING_ID, id, create);
}
key_serial_t keyctl_join_session_keyring(const char *name)
{
return keyctl(KEYCTL_JOIN_SESSION_KEYRING, name);
}
long keyctl_update(key_serial_t id, const void *payload, size_t plen)
{
return keyctl(KEYCTL_UPDATE, id, payload, plen);
}
long keyctl_revoke(key_serial_t id)
{
return keyctl(KEYCTL_REVOKE, id);
}
long keyctl_chown(key_serial_t id, uid_t uid, gid_t gid)
{
return keyctl(KEYCTL_CHOWN, id, uid, gid);
}
long keyctl_setperm(key_serial_t id, key_perm_t perm)
{
return keyctl(KEYCTL_SETPERM, id, perm);
}
long keyctl_describe(key_serial_t id, char *buffer, size_t buflen)
{
return keyctl(KEYCTL_DESCRIBE, id, buffer, buflen);
}
long keyctl_clear(key_serial_t ringid)
{
return keyctl(KEYCTL_CLEAR, ringid);
}
long keyctl_link(key_serial_t id, key_serial_t ringid)
{
return keyctl(KEYCTL_LINK, id, ringid);
}
long keyctl_unlink(key_serial_t id, key_serial_t ringid)
{
return keyctl(KEYCTL_UNLINK, id, ringid);
}
long keyctl_search(key_serial_t ringid,
const char *type,
const char *description,
key_serial_t destringid)
{
return keyctl(KEYCTL_SEARCH, ringid, type, description, destringid);
}
long keyctl_read(key_serial_t id, char *buffer, size_t buflen)
{
return keyctl(KEYCTL_READ, id, buffer, buflen);
}
long keyctl_instantiate(key_serial_t id,
const void *payload,
size_t plen,
key_serial_t ringid)
{
return keyctl(KEYCTL_INSTANTIATE, id, payload, plen, ringid);
}
long keyctl_negate(key_serial_t id, unsigned timeout, key_serial_t ringid)
{
return keyctl(KEYCTL_NEGATE, id, timeout, ringid);
}
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
return keyctl(KEYCTL_SET_REQKEY_KEYRING, reqkey_defl);
}
long keyctl_set_timeout(key_serial_t id, unsigned timeout)
{
return keyctl(KEYCTL_SET_TIMEOUT, id, timeout);
}
long keyctl_assume_authority(key_serial_t id)
{
return keyctl(KEYCTL_ASSUME_AUTHORITY, id);
}
long keyctl_get_security(key_serial_t id, char *buffer, size_t buflen)
{
return keyctl(KEYCTL_GET_SECURITY, id, buffer, buflen);
}
long keyctl_session_to_parent(void)
{
return keyctl(KEYCTL_SESSION_TO_PARENT);
}
/*****************************************************************************/
/*
* fetch key description into an allocated buffer
* - resulting string is NUL terminated
* - returns count not including NUL
*/
int keyctl_describe_alloc(key_serial_t id, char **_buffer)
{
char *buf;
long buflen, ret;
ret = keyctl_describe(id, NULL, 0);
if (ret < 0)
return -1;
buflen = ret;
buf = malloc(buflen);
if (!buf)
return -1;
for (;;) {
ret = keyctl_describe(id, buf, buflen);
if (ret < 0)
return -1;
if (buflen >= ret)
break;
buflen = ret;
buf = realloc(buf, buflen);
if (!buf)
return -1;
}
*_buffer = buf;
return buflen - 1;
} /* end keyctl_describe_alloc() */
/*****************************************************************************/
/*
* fetch key contents into an allocated buffer
* - resulting buffer has an extra NUL added to the end
* - returns count (not including extraneous NUL)
*/
int keyctl_read_alloc(key_serial_t id, void **_buffer)
{
void *buf;
long buflen, ret;
ret = keyctl_read(id, NULL, 0);
if (ret < 0)
return -1;
buflen = ret;
buf = malloc(buflen + 1);
if (!buf)
return -1;
for (;;) {
ret = keyctl_read(id, buf, buflen);
if (ret < 0)
return -1;
if (buflen >= ret)
break;
buflen = ret;
buf = realloc(buf, buflen + 1);
if (!buf)
return -1;
}
((unsigned char *) buf)[buflen] = 0;
*_buffer = buf;
return buflen;
} /* end keyctl_read_alloc() */
/*****************************************************************************/
/*
* fetch key security label into an allocated buffer
* - resulting string is NUL terminated
* - returns count not including NUL
*/
int keyctl_get_security_alloc(key_serial_t id, char **_buffer)
{
char *buf;
long buflen, ret;
ret = keyctl_get_security(id, NULL, 0);
if (ret < 0)
return -1;
buflen = ret;
buf = malloc(buflen);
if (!buf)
return -1;
for (;;) {
ret = keyctl_get_security(id, buf, buflen);
if (ret < 0)
return -1;
if (buflen >= ret)
break;
buflen = ret;
buf = realloc(buf, buflen);
if (!buf)
return -1;
}
*_buffer = buf;
return buflen - 1;
}
#ifdef NO_GLIBC_KEYERR
/*****************************************************************************/
/*
* initialise error handling
*/
static void error_init(void)
{
char *err;
error_inited = 1;
dlerror();
libc_perror = dlsym(RTLD_NEXT,"perror");
if (!libc_perror) {
fprintf(stderr, "Failed to look up next perror\n");
err = dlerror();
if (err)
fprintf(stderr, "%s\n", err);
abort();
}
//fprintf(stderr, "next perror at %p\n", libc_perror);
libc_strerror_r = dlsym(RTLD_NEXT,"strerror_r");
if (!libc_strerror_r) {
fprintf(stderr, "Failed to look up next strerror_r\n");
err = dlerror();
if (err)
fprintf(stderr, "%s\n", err);
abort();
}
//fprintf(stderr, "next strerror_r at %p\n", libc_strerror_r);
#if 0
libc_xpg_strerror_r = dlsym(RTLD_NEXT,"xpg_strerror_r");
if (!libc_xpg_strerror_r) {
fprintf(stderr, "Failed to look up next xpg_strerror_r\n");
err = dlerror();
if (err)
fprintf(stderr, "%s\n", err);
abort();
}
//fprintf(stderr, "next xpg_strerror_r at %p\n", libc_xpg_strerror_r);
#endif
} /* end error_init() */
/*****************************************************************************/
/*
* overload glibc's strerror_r() with a version that knows about key errors
*/
char *strerror_r(int errnum, char *buf, size_t n)
{
const char *errstr;
int len;
printf("hello\n");
if (!error_inited)
error_init();
switch (errnum) {
case ENOKEY:
errstr = "Requested key not available";
break;
case EKEYEXPIRED:
errstr = "Key has expired";
break;
case EKEYREVOKED:
errstr = "Key has been revoked";
break;
case EKEYREJECTED:
errstr = "Key was rejected by service";
break;
default:
return libc_strerror_r(errnum, buf, n);
}
len = strlen(errstr) + 1;
if (n > len) {
errno = ERANGE;
if (n > 0) {
memcpy(buf, errstr, n - 1);
buf[n - 1] = 0;
}
return NULL;
}
else {
memcpy(buf, errstr, len);
return buf;
}
} /* end strerror_r() */
#if 0
/*****************************************************************************/
/*
* overload glibc's strerror_r() with a version that knows about key errors
*/
int xpg_strerror_r(int errnum, char *buf, size_t n)
{
const char *errstr;
int len;
if (!error_inited)
error_init();
switch (errnum) {
case ENOKEY:
errstr = "Requested key not available";
break;
case EKEYEXPIRED:
errstr = "Key has expired";
break;
case EKEYREVOKED:
errstr = "Key has been revoked";
break;
case EKEYREJECTED:
errstr = "Key was rejected by service";
break;
default:
return libc_xpg_strerror_r(errnum, buf, n);
}
len = strlen(errstr) + 1;
if (n > len) {
errno = ERANGE;
if (n > 0) {
memcpy(buf, errstr, n - 1);
buf[n - 1] = 0;
}
return -1;
}
else {
memcpy(buf, errstr, len);
return 0;
}
} /* end xpg_strerror_r() */
#endif
/*****************************************************************************/
/*
*
*/
void perror(const char *msg)
{
if (!error_inited)
error_init();
switch (errno) {
case ENOKEY:
fprintf(stderr, "%s: Requested key not available\n", msg);
return;
case EKEYEXPIRED:
fprintf(stderr, "%s: Key has expired\n", msg);
return;
case EKEYREVOKED:
fprintf(stderr, "%s: Key has been revoked\n", msg);
return;
case EKEYREJECTED:
fprintf(stderr, "%s: Key was rejected by service\n", msg);
return;
default:
libc_perror(msg);
return;
}
} /* end perror() */
#endif