/* keyutils.c: key utility library * * Copyright (C) 2005,2011 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 #include #include #include #include #include #include #include #include #include #include "keyutils.h" const char keyutils_version_string[] = PKGVERSION; const char keyutils_build_string[] = PKGBUILD; #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); } long keyctl_reject(key_serial_t id, unsigned timeout, unsigned error, key_serial_t ringid) { long ret = keyctl(KEYCTL_REJECT, id, timeout, error, ringid); /* fall back to keyctl_negate() if this op is not supported by this * kernel version */ if (ret == -1 && errno == EOPNOTSUPP) return keyctl_negate(id, timeout, ringid); return ret; } long keyctl_instantiate_iov(key_serial_t id, const struct iovec *payload_iov, unsigned ioc, key_serial_t ringid) { long ret = keyctl(KEYCTL_INSTANTIATE_IOV, id, payload_iov, ioc, ringid); /* fall back to keyctl_instantiate() if this op is not supported by * this kernel version */ if (ret == -1 && errno == EOPNOTSUPP) { unsigned loop; size_t bsize = 0, seg; void *buf, *p; if (!payload_iov || !ioc) return keyctl_instantiate(id, NULL, 0, ringid); for (loop = 0; loop < ioc; loop++) bsize += payload_iov[loop].iov_len; if (bsize == 0) return keyctl_instantiate(id, NULL, 0, ringid); p = buf = malloc(bsize); if (!buf) return -1; for (loop = 0; loop < ioc; loop++) { seg = payload_iov[loop].iov_len; p = memcpy(p, payload_iov[loop].iov_base, seg) + seg; } ret = keyctl_instantiate(id, buf, bsize, ringid); free(buf); } return ret; } long keyctl_invalidate(key_serial_t id) { return keyctl(KEYCTL_INVALIDATE, id); } /*****************************************************************************/ /* * 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; } /* * Depth-first recursively apply a function over a keyring tree */ static int recursive_key_scan_aux(key_serial_t parent, key_serial_t key, int depth, recursive_key_scanner_t func, void *data) { key_serial_t *pk; key_perm_t perm; size_t ringlen; void *ring; char *desc, type[255]; int desc_len, uid, gid, ret, n, kcount = 0; if (depth > 800) return 0; /* read the key description */ desc = NULL; desc_len = keyctl_describe_alloc(key, &desc); if (desc_len < 0) goto do_this_key; /* parse */ type[0] = 0; n = sscanf(desc, "%[^;];%d;%d;%x;", type, &uid, &gid, &perm); if (n != 4) { free(desc); desc = NULL; errno = -EINVAL; desc_len = -1; goto do_this_key; } /* if it's a keyring then we're going to want to recursively search it * if we can */ if (strcmp(type, "keyring") == 0) { /* read the keyring's contents */ ret = keyctl_read_alloc(key, &ring); if (ret < 0) goto do_this_key; ringlen = ret; /* walk the keyring */ pk = ring; for (ringlen = ret; ringlen >= sizeof(key_serial_t); ringlen -= sizeof(key_serial_t) ) kcount += recursive_key_scan_aux(key, *pk++, depth + 1, func, data); free(ring); } do_this_key: kcount += func(parent, key, desc, desc_len, data); free(desc); return kcount; } /* * Depth-first apply a function over a keyring tree */ int recursive_key_scan(key_serial_t key, recursive_key_scanner_t func, void *data) { return recursive_key_scan_aux(0, key, 0, func, data); } /* * Depth-first apply a function over session keyring tree */ int recursive_session_key_scan(recursive_key_scanner_t func, void *data) { key_serial_t session = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0); if (session > 0) return recursive_key_scan(session, func, data); return 0; } #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