1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
/*
* Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved.
*
* Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
*
* This software licensed under LGPL-2.0+
*/
#include "config.h"
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include <libgen.h>
#include <link.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include "logging.h"
#include "common.h"
int _fdset_cloexec(int fd)
{
int fdflags;
fdflags = fcntl(fd, F_GETFD, 0);
if (fdflags < 0)
return -1;
fdflags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, fdflags) < 0)
return -1;
return 0;
}
int _fdset_nonblock(int fd)
{
int fdflags;
fdflags = fcntl(fd, F_GETFL, 0);
if (fdflags < 0)
return -1;
fdflags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, fdflags) < 0)
return -1;
return 0;
}
static int get_lib_dir(void *lib_handle, char dir[MAXPATHLEN])
{
int res;
#ifndef HAVE_RTLD_DI_ORIGIN
struct link_map *lm;
char l_name[MAXPATHLEN];
#endif
#ifdef HAVE_RTLD_DI_ORIGIN
res = dlinfo(lib_handle, RTLD_DI_ORIGIN, dir);
#else
/*
* musl libc doesn't support RTLD_DI_ORIGIN
*/
res = dlinfo(lib_handle, RTLD_DI_LINKMAP, &lm);
if (res == 0) {
snprintf(l_name, sizeof(l_name), "%s", lm->l_name);
snprintf(dir, MAXPATHLEN, "%s", dirname(l_name));
}
#endif
return res;
}
static void *open_lib(knet_handle_t knet_h, const char *libname, int extra_flags)
{
void *ret = NULL;
char *error = NULL;
char dir[MAXPATHLEN], path[MAXPATHLEN * 2], link[MAXPATHLEN];
struct stat sb;
/*
* clear any pending error
*/
dlerror();
strncpy(path, knet_h->plugin_path, sizeof(path)-1);
strncat(path, "/", sizeof(path)-1);
strncat(path, libname, sizeof(path)-strlen(knet_h->plugin_path)-2);
ret = dlopen(path, RTLD_NOW | RTLD_GLOBAL | extra_flags);
if (!ret) {
error = dlerror();
if (error) {
log_err(knet_h, KNET_SUB_COMMON, "unable to dlopen %s: %s", libname, error);
} else {
log_err(knet_h, KNET_SUB_COMMON, "unable to dlopen %s: unknown error", libname);
}
errno = EAGAIN;
return NULL;
}
memset(dir, 0, sizeof(dir));
memset(link, 0, sizeof(link));
memset(path, 0, sizeof(path));
if (get_lib_dir(ret, dir) < 0) {
/*
* should we dlclose and return error?
*/
error = dlerror();
log_warn(knet_h, KNET_SUB_COMMON, "unable to dlinfo %s: %s",
libname, error);
} else {
snprintf(path, sizeof(path), "%s/%s", dir, libname);
log_info(knet_h, KNET_SUB_COMMON, "%s has been loaded from %s", libname, path);
/*
* try to resolve the library and check if it is a symlink and to where.
* we can't prevent symlink attacks but at least we can log where the library
* has been loaded from
*/
if (lstat(path, &sb) < 0) {
log_debug(knet_h, KNET_SUB_COMMON, "Unable to stat %s: %s", path, strerror(errno));
goto out;
}
if (S_ISLNK(sb.st_mode)) {
if (readlink(path, link, sizeof(link)-1) < 0) {
log_debug(knet_h, KNET_SUB_COMMON, "Unable to readlink %s: %s", path, strerror(errno));
goto out;
}
link[sizeof(link) - 1] = 0;
/*
* symlink is relative to the directory
*/
if (link[0] != '/') {
snprintf(path, sizeof(path), "%s/%s", dir, link);
log_info(knet_h, KNET_SUB_COMMON, "%s/%s is a symlink to %s", dir, libname, path);
} else {
log_info(knet_h, KNET_SUB_COMMON, "%s/%s is a symlink to %s", dir, libname, link);
}
}
}
out:
return ret;
}
void *load_module(knet_handle_t knet_h, const char *type, const char *name)
{
void *module, *ops;
log_msg_t **log_msg_sym;
char soname[MAXPATHLEN], opsname[MAXPATHLEN];
snprintf (soname, sizeof soname, "%s_%s.so", type, name);
module = open_lib(knet_h, soname, 0);
if (!module) {
return NULL;
}
log_msg_sym = dlsym (module, "log_msg");
if (!log_msg_sym) {
log_err (knet_h, KNET_SUB_COMMON, "unable to map symbol 'log_msg' in module %s: %s",
soname, dlerror ());
errno = EINVAL;
return NULL;
}
*log_msg_sym = log_msg;
snprintf (opsname, sizeof opsname, "%s_model", type);
ops = dlsym (module, opsname);
if (!ops) {
log_err (knet_h, KNET_SUB_COMMON, "unable to map symbol 'model' in module %s: %s",
soname, dlerror ());
errno = EINVAL;
return NULL;
}
return ops;
}
|