Coverage Report

Created: 2025-12-27 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/agent/kernel.c
Line
Count
Source
1
/***********************************************************************
2
   Net-SNMP - Simple Network Management Protocol agent library.
3
 ***********************************************************************/
4
/** @file kernel.c
5
 *     Net-SNMP Kernel Data Access Library.
6
 *     Provides access to kernel virtual memory for systems that
7
 *     support it.
8
 * @author   See README file for a list of contributors
9
 */
10
/* Copyrights:
11
 *     Copyright holders are listed in README file.
12
 *     Redistribution and use in source and binary forms, with or
13
 *     without modification, are permitted. License terms are specified
14
 *     in COPYING file distributed with the Net-SNMP package.
15
 */
16
/***********************************************************************/
17
18
#include <net-snmp/net-snmp-config.h>
19
20
#include <sys/types.h>
21
#ifdef HAVE_STDLIB_H
22
#include <stdlib.h>
23
#endif
24
#ifdef HAVE_UNISTD_H
25
#include <unistd.h>
26
#endif
27
#include <stdio.h>
28
#include <errno.h>
29
#ifdef HAVE_STRING_H
30
#include <string.h>
31
#endif
32
#ifdef HAVE_FCNTL_H
33
#include <fcntl.h>
34
#endif
35
#ifdef HAVE_NETINET_IN_H
36
#include <netinet/in.h>
37
#endif
38
#ifdef HAVE_KVM_H
39
#include <kvm.h>
40
#endif
41
42
#include <net-snmp/net-snmp-includes.h>
43
44
#include "kernel.h"
45
#include <net-snmp/agent/ds_agent.h>
46
47
#if defined(HAVE_KVM_H) && !defined(NETSNMP_NO_KMEM_USAGE) && !defined(__FreeBSD__)
48
kvm_t *kd;
49
50
/**
51
 * Initialize the support for accessing kernel virtual memory.
52
 *
53
 * @return TRUE upon success; FALSE upon failure.
54
 */
55
int
56
init_kmem(const char *file)
57
{
58
    int res = TRUE;
59
60
#ifdef HAVE_KVM_OPENFILES
61
    char            err[4096];
62
63
    kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, err);
64
    if (!kd)
65
#ifdef KVM_NO_FILES
66
  kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, err);
67
#else
68
  kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, err);
69
#endif
70
    if (!kd && !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
71
                                       NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
72
        snmp_log(LOG_CRIT, "init_kmem: kvm_openfiles failed: %s\n", err);
73
        res = FALSE;
74
    }
75
#else
76
    kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
77
    if (!kd && !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
78
               NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
79
        snmp_log(LOG_CRIT, "init_kmem: kvm_open failed: %s\n",
80
                 strerror(errno));
81
        res = FALSE;
82
    }
83
#endif                          /* HAVE_KVM_OPENFILES */
84
    return res;
85
}
86
87
/** Reads kernel memory.
88
 *  Seeks to the specified location in kmem, then
89
 *  does a read of given amount ob bytes into target buffer.
90
 *
91
 * @param off The location to seek.
92
 *
93
 * @param target The target buffer to read into.
94
 *
95
 * @param siz Number of bytes to read.
96
 *
97
 * @return gives 1 on success and 0 on failure.
98
 */
99
int
100
klookup(unsigned long off, void *target, size_t siz)
101
{
102
    int             result;
103
104
    if (kd == NULL)
105
        return 0;
106
    result = kvm_read(kd, off, target, siz);
107
    if (result != siz) {
108
#ifdef HAVE_KVM_OPENFILES
109
        snmp_log(LOG_ERR, "kvm_read(*, %lx, %p, %x) = %d: %s\n", off,
110
                 target, (unsigned) siz, result, kvm_geterr(kd));
111
#else
112
        snmp_log(LOG_ERR, "kvm_read(*, %lx, %p, %d) = %d: ", off, target,
113
                 (unsigned) siz, result);
114
        snmp_log_perror("klookup");
115
#endif
116
        return 0;
117
    }
118
    return 1;
119
}
120
121
/** Closes the kernel memory support.
122
 */
123
void
124
free_kmem(void)
125
{
126
    if (kd != NULL)
127
    {
128
      kvm_close(kd);
129
      kd = NULL;
130
    }
131
}
132
133
#elif defined(HAVE_NLIST_H) && !defined(__linux__) && !defined(__FreeBSD__) && \
134
    !defined(NETSNMP_NO_KMEM_USAGE)
135
136
static off_t    klseek(off_t);
137
static int      klread(char *, int);
138
int             swap = -1, mem = -1, kmem = -1;
139
140
static void netsnmp_cloexec(int fd)
141
{
142
    if (fd < 0)
143
        return;
144
#ifdef HAVE_FD_CLOEXEC
145
    if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
146
        snmp_log_perror("fcntl(FD_CLOEXEC)");
147
#endif
148
}
149
150
/**
151
 * Initialize the support for accessing kernel virtual memory.
152
 *
153
 * @return TRUE upon success; FALSE upon failure.
154
 */
155
int
156
init_kmem(const char *file)
157
{
158
    const int no_root_access = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
159
                                              NETSNMP_DS_AGENT_NO_ROOT_ACCESS);
160
    int res = TRUE;
161
162
    kmem = open(file, O_RDONLY);
163
    if (kmem < 0 && !no_root_access) {
164
        snmp_log_perror(file);
165
        res = FALSE;
166
    }
167
    netsnmp_cloexec(kmem);
168
    mem = open("/dev/mem", O_RDONLY);
169
    if (mem < 0 && !no_root_access) {
170
        snmp_log_perror("/dev/mem");
171
        res = FALSE;
172
    }
173
    netsnmp_cloexec(mem);
174
#ifdef DMEM_LOC
175
    swap = open(DMEM_LOC, O_RDONLY);
176
    if (swap < 0 && !no_root_access) {
177
        snmp_log_perror(DMEM_LOC);
178
        res = FALSE;
179
    }
180
    netsnmp_cloexec(swap);
181
#endif
182
    return res;
183
}
184
185
/** @private
186
 *  Seek into the kernel for a value.
187
 */
188
static off_t
189
klseek(off_t base)
190
{
191
    return (lseek(kmem, (off_t) base, SEEK_SET));
192
}
193
194
/** @private
195
 *  Read from the kernel.
196
 */
197
static int
198
klread(char *buf, int buflen)
199
{
200
    return (read(kmem, buf, buflen));
201
}
202
203
/** Reads kernel memory.
204
 *  Seeks to the specified location in kmem, then
205
 *  does a read of given amount ob bytes into target buffer.
206
 *
207
 * @param off The location to seek.
208
 *
209
 * @param target The target buffer to read into.
210
 *
211
 * @param siz Number of bytes to read.
212
 *
213
 * @return gives 1 on success and 0 on failure.
214
 */
215
int
216
klookup(unsigned long off, void *target, size_t siz)
217
{
218
    long            retsiz;
219
220
    if (kmem < 0)
221
        return 0;
222
223
    if ((retsiz = klseek((off_t) off)) != off) {
224
        snmp_log(LOG_ERR, "klookup(%lx, %p, %d): ", off, target, (int) siz);
225
        snmp_log_perror("klseek");
226
        return (0);
227
    }
228
    if ((retsiz = klread(target, siz)) != siz) {
229
        if (snmp_get_do_debugging()) {
230
            /*
231
             * these happen too often on too many architectures to print them
232
             * unless we're in debugging mode. People get very full log files. 
233
             */
234
            snmp_log(LOG_ERR, "klookup(%lx, %p, %d): ", off, target, (int) siz);
235
            snmp_log_perror("klread");
236
        }
237
        return (0);
238
    }
239
    DEBUGMSGTL(("verbose:kernel:klookup", "klookup(%lx, %p, %d) succeeded",
240
                off, target, (int) siz));
241
    return (1);
242
}
243
244
/** Closes the kernel memory support.
245
 */
246
void
247
free_kmem(void)
248
{
249
    if (swap >= 0) {
250
        close(swap);
251
        swap = -1;
252
    }
253
    if (mem >= 0) {
254
        close(mem);
255
        mem = -1;
256
    }
257
    if (kmem >= 0) {
258
        close(kmem);
259
        kmem = -1;
260
    }
261
}
262
#elif defined(__FreeBSD__)
263
kvm_t *kd;
264
265
/**
266
 * Initialize the libkvm descriptor. On FreeBSD we can use most of libkvm
267
 * without requiring /dev/kmem access.  Only kvm_nlist() and kvm_read() need
268
 * that, and we don't use them.
269
 *
270
 * @return TRUE upon success; FALSE upon failure.
271
 */
272
int
273
init_kmem(const char *file)
274
{
275
    char err[4096];
276
277
    kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, err);
278
    if (!kd) {
279
        snmp_log(LOG_CRIT, "init_kmem: kvm_openfiles failed: %s\n", err);
280
        return FALSE;
281
    }
282
    return TRUE;
283
}
284
285
/**
286
 * A stub to return failure to any attempt to read kernel memory.  Our
287
 * libkvm handle doesn't enable /dev/kmem access.  MIB implementations should
288
 * use unprivileged to fetch information about the system.
289
 */
290
int
291
klookup(unsigned long off, void *target, size_t siz)
292
{
293
    return 0;
294
}
295
296
void
297
free_kmem(void)
298
{
299
    if (kd != NULL) {
300
        (void)kvm_close(kd);
301
        kd = NULL;
302
    }
303
}
304
#else
305
int
306
init_kmem(const char *file)
307
0
{
308
0
    return 1;  /* success */
309
0
}
310
311
void
312
free_kmem(void)
313
0
{
314
0
}
315
#endif