Deleted Added
full compact
main.c (216294) main.c (216594)
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Copyright (c) 2010 The FreeBSD Foundation
9 * All rights reserved.
10 *
11 * Portions of this software were developed by Shteryana Sotirova Shopova
12 * under sponsorship from the FreeBSD Foundation.
13 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $
30 *
31 * SNMPd main stuff.
32 */
33
34#include <sys/queue.h>
35#include <sys/param.h>
36#include <sys/un.h>
37#include <sys/ucred.h>
38#include <sys/uio.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <stddef.h>
42#include <string.h>
43#include <stdarg.h>
44#include <ctype.h>
45#include <errno.h>
46#include <syslog.h>
47#include <unistd.h>
48#include <signal.h>
49#include <dlfcn.h>
50#include <inttypes.h>
51
52#ifdef USE_TCPWRAPPERS
53#include <arpa/inet.h>
54#include <tcpd.h>
55#endif
56
57#include "support.h"
58#include "snmpmod.h"
59#include "snmpd.h"
60#include "tree.h"
61#include "oid.h"
62
63#define PATH_PID "/var/run/%s.pid"
64#define PATH_CONFIG "/etc/%s.config"
65#define PATH_ENGINE "/var/%s.engine"
66
67uint64_t this_tick; /* start of processing of current packet (absolute) */
68uint64_t start_tick; /* start of processing */
69
70struct systemg systemg = {
71 NULL,
72 { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }},
73 NULL, NULL, NULL,
74 64 + 8 + 4,
75 0
76};
77struct debug debug = {
78 0, /* dump_pdus */
79 LOG_DEBUG, /* log_pri */
80 0, /* evdebug */
81};
82
83struct snmpd snmpd = {
84 2048, /* txbuf */
85 2048, /* rxbuf */
86 0, /* comm_dis */
87 0, /* auth_traps */
88 {0, 0, 0, 0}, /* trap1addr */
89 VERS_ENABLE_ALL,/* version_enable */
90};
91struct snmpd_stats snmpd_stats;
92
93struct snmpd_usmstat snmpd_usmstats;
94
95/* snmpEngine */
96struct snmp_engine snmpd_engine;
97
98/* snmpSerialNo */
99int32_t snmp_serial_no;
100
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $
36 *
37 * SNMPd main stuff.
38 */
39
40#include <sys/queue.h>
41#include <sys/param.h>
42#include <sys/un.h>
43#include <sys/ucred.h>
44#include <sys/uio.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <stddef.h>
48#include <string.h>
49#include <stdarg.h>
50#include <ctype.h>
51#include <errno.h>
52#include <syslog.h>
53#include <unistd.h>
54#include <signal.h>
55#include <dlfcn.h>
56#include <inttypes.h>
57
58#ifdef USE_TCPWRAPPERS
59#include <arpa/inet.h>
60#include <tcpd.h>
61#endif
62
63#include "support.h"
64#include "snmpmod.h"
65#include "snmpd.h"
66#include "tree.h"
67#include "oid.h"
68
69#define PATH_PID "/var/run/%s.pid"
70#define PATH_CONFIG "/etc/%s.config"
71#define PATH_ENGINE "/var/%s.engine"
72
73uint64_t this_tick; /* start of processing of current packet (absolute) */
74uint64_t start_tick; /* start of processing */
75
76struct systemg systemg = {
77 NULL,
78 { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }},
79 NULL, NULL, NULL,
80 64 + 8 + 4,
81 0
82};
83struct debug debug = {
84 0, /* dump_pdus */
85 LOG_DEBUG, /* log_pri */
86 0, /* evdebug */
87};
88
89struct snmpd snmpd = {
90 2048, /* txbuf */
91 2048, /* rxbuf */
92 0, /* comm_dis */
93 0, /* auth_traps */
94 {0, 0, 0, 0}, /* trap1addr */
95 VERS_ENABLE_ALL,/* version_enable */
96};
97struct snmpd_stats snmpd_stats;
98
99struct snmpd_usmstat snmpd_usmstats;
100
101/* snmpEngine */
102struct snmp_engine snmpd_engine;
103
104/* snmpSerialNo */
105int32_t snmp_serial_no;
106
107struct snmpd_target_stats snmpd_target_stats;
108
101/* search path for config files */
102const char *syspath = PATH_SYSCONFIG;
103
104/* list of all loaded modules */
105struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules);
106
107/* list of loaded modules during start-up in the order they were loaded */
108static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start);
109
110/* list of all known communities */
111struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list);
112
113/* list of all known USM users */
114struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist);
115
116/* A list of all VACM users configured, including v1, v2c and v3 */
117struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist);
118
119/* A list of all VACM groups */
120struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist);
121
122static struct vacm_group vacm_default_group = {
123 .groupname = "",
124};
125
126/* The list of configured access entries */
127struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist);
128
129/* The list of configured views */
130struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist);
131
132/* The list of configured contexts */
133struct vacm_contextlist vacm_contextlist =
134 SLIST_HEAD_INITIALIZER(vacm_contextlist);
135
136/* list of all installed object resources */
137struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list);
138
139/* community value generator */
140static u_int next_community_index = 1;
141
142/* list of all known ranges */
143struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list);
144
145/* identifier generator */
146u_int next_idrange = 1;
147
148/* list of all current timers */
149struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list);
150
151/* list of file descriptors */
152struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list);
153
154/* program arguments */
155static char **progargs;
156static int nprogargs;
157
158/* current community */
159u_int community;
160static struct community *comm;
161
162/* current USM user */
163struct usm_user *usm_user;
164
165/* file names */
166static char config_file[MAXPATHLEN + 1];
167static char pid_file[MAXPATHLEN + 1];
168char engine_file[MAXPATHLEN + 1];
169
170#ifndef USE_LIBBEGEMOT
171/* event context */
172static evContext evctx;
173#endif
174
175/* signal mask */
176static sigset_t blocked_sigs;
177
178/* signal handling */
179static int work;
180#define WORK_DOINFO 0x0001
181#define WORK_RECONFIG 0x0002
182
183/* oids */
184static const struct asn_oid
185 oid_snmpMIB = OIDX_snmpMIB,
186 oid_begemotSnmpd = OIDX_begemotSnmpd,
187 oid_coldStart = OIDX_coldStart,
188 oid_authenticationFailure = OIDX_authenticationFailure;
189
190const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }};
191
192const struct asn_oid oid_usmUnknownEngineIDs =
193 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}};
194
195const struct asn_oid oid_usmNotInTimeWindows =
196 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}};
197
198/* request id generator for traps */
199u_int trap_reqid;
200
201/* help text */
202static const char usgtxt[] = "\
203Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
204Open Communication Systems (FhG Fokus). All rights reserved.\n\
205Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\
206usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\
207 [-l prefix] [-m variable=value] [-p file]\n\
208options:\n\
209 -d don't daemonize\n\
210 -h print this info\n\
211 -c file specify configuration file\n\
212 -D options debugging options\n\
213 -e file specify engine id file\n\
214 -I path system include path\n\
215 -l prefix default basename for pid and config file\n\
216 -m var=val define variable\n\
217 -p file specify pid file\n\
218";
219
220/* hosts_access(3) request */
221#ifdef USE_TCPWRAPPERS
222static struct request_info req;
223#endif
224
225/* transports */
226extern const struct transport_def udp_trans;
227extern const struct transport_def lsock_trans;
228
229struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list);
230
231/* forward declarations */
232static void snmp_printf_func(const char *fmt, ...);
233static void snmp_error_func(const char *err, ...);
234static void snmp_debug_func(const char *err, ...);
235static void asn_error_func(const struct asn_buf *b, const char *err, ...);
236
237/*
238 * Allocate rx/tx buffer. We allocate one byte more for rx.
239 */
240void *
241buf_alloc(int tx)
242{
243 void *buf;
244
245 if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) {
246 syslog(LOG_CRIT, "cannot allocate buffer");
247 if (tx)
248 snmpd_stats.noTxbuf++;
249 else
250 snmpd_stats.noRxbuf++;
251 return (NULL);
252 }
253 return (buf);
254}
255
256/*
257 * Return the buffer size.
258 */
259size_t
260buf_size(int tx)
261{
262 return (tx ? snmpd.txbuf : snmpd.rxbuf);
263}
264
265/*
266 * Prepare a PDU for output
267 */
268void
269snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen,
270 const char *dest)
271{
272 struct asn_buf resp_b;
273
274 resp_b.asn_ptr = sndbuf;
275 resp_b.asn_len = snmpd.txbuf;
276
277 if (snmp_pdu_encode(pdu, &resp_b) != 0) {
278 syslog(LOG_ERR, "cannot encode message");
279 abort();
280 }
281 if (debug.dump_pdus) {
282 snmp_printf("%s <- ", dest);
283 snmp_pdu_dump(pdu);
284 }
285 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
286}
287
288/*
289 * Check USM PDU header credentials against local SNMP Engine & users.
290 */
291static enum snmp_code
292snmp_pdu_auth_user(struct snmp_pdu *pdu)
293{
294 uint64_t etime;
295 usm_user = NULL;
296
297 /* un-authenticated snmpEngineId discovery */
298 if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) {
299 pdu->engine.engine_len = snmpd_engine.engine_len;
300 memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
301 snmpd_engine.engine_len);
302 pdu->engine.engine_boots = snmpd_engine.engine_boots;
303 pdu->engine.engine_time = snmpd_engine.engine_time;
304 pdu->flags |= SNMP_MSG_AUTODISCOVER;
305 return (SNMP_CODE_OK);
306 }
307
308 if ((usm_user = usm_find_user(pdu->engine.engine_id,
309 pdu->engine.engine_len, pdu->user.sec_name)) == NULL ||
310 usm_user->status != 1 /* active */)
311 return (SNMP_CODE_BADUSER);
312
313 if (usm_user->user_engine_len != snmpd_engine.engine_len ||
314 memcmp(usm_user->user_engine_id, snmpd_engine.engine_id,
315 snmpd_engine.engine_len) != 0)
316 return (SNMP_CODE_BADENGINE);
317
318 pdu->user.priv_proto = usm_user->suser.priv_proto;
319 memcpy(pdu->user.priv_key, usm_user->suser.priv_key,
320 sizeof(pdu->user.priv_key));
321
322 /* authenticated snmpEngineId discovery */
323 if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
324 etime = (get_ticks() - start_tick) / 100ULL;
325 if (etime < INT32_MAX)
326 snmpd_engine.engine_time = etime;
327 else {
328 start_tick = get_ticks();
329 set_snmpd_engine();
330 snmpd_engine.engine_time = start_tick;
331 }
332
333 pdu->user.auth_proto = usm_user->suser.auth_proto;
334 memcpy(pdu->user.auth_key, usm_user->suser.auth_key,
335 sizeof(pdu->user.auth_key));
336
337 if (pdu->engine.engine_boots == 0 &&
338 pdu->engine.engine_time == 0) {
339 pdu->flags |= SNMP_MSG_AUTODISCOVER;
340 return (SNMP_CODE_OK);
341 }
342
343 if (pdu->engine.engine_boots != snmpd_engine.engine_boots ||
344 abs(pdu->engine.engine_time - snmpd_engine.engine_time) >
345 SNMP_TIME_WINDOW)
346 return (SNMP_CODE_NOTINTIME);
347 }
348
349 if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 &&
350 (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) ||
351 ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 &&
352 usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) ||
353 ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 &&
354 usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV))
355 return (SNMP_CODE_BADSECLEVEL);
356
357 return (SNMP_CODE_OK);
358}
359
360/*
361 * Check whether access to each of var bindings in the PDU is allowed based
362 * on the user credentials against the configured User groups & VACM views.
363 */
109/* search path for config files */
110const char *syspath = PATH_SYSCONFIG;
111
112/* list of all loaded modules */
113struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules);
114
115/* list of loaded modules during start-up in the order they were loaded */
116static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start);
117
118/* list of all known communities */
119struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list);
120
121/* list of all known USM users */
122struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist);
123
124/* A list of all VACM users configured, including v1, v2c and v3 */
125struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist);
126
127/* A list of all VACM groups */
128struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist);
129
130static struct vacm_group vacm_default_group = {
131 .groupname = "",
132};
133
134/* The list of configured access entries */
135struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist);
136
137/* The list of configured views */
138struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist);
139
140/* The list of configured contexts */
141struct vacm_contextlist vacm_contextlist =
142 SLIST_HEAD_INITIALIZER(vacm_contextlist);
143
144/* list of all installed object resources */
145struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list);
146
147/* community value generator */
148static u_int next_community_index = 1;
149
150/* list of all known ranges */
151struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list);
152
153/* identifier generator */
154u_int next_idrange = 1;
155
156/* list of all current timers */
157struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list);
158
159/* list of file descriptors */
160struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list);
161
162/* program arguments */
163static char **progargs;
164static int nprogargs;
165
166/* current community */
167u_int community;
168static struct community *comm;
169
170/* current USM user */
171struct usm_user *usm_user;
172
173/* file names */
174static char config_file[MAXPATHLEN + 1];
175static char pid_file[MAXPATHLEN + 1];
176char engine_file[MAXPATHLEN + 1];
177
178#ifndef USE_LIBBEGEMOT
179/* event context */
180static evContext evctx;
181#endif
182
183/* signal mask */
184static sigset_t blocked_sigs;
185
186/* signal handling */
187static int work;
188#define WORK_DOINFO 0x0001
189#define WORK_RECONFIG 0x0002
190
191/* oids */
192static const struct asn_oid
193 oid_snmpMIB = OIDX_snmpMIB,
194 oid_begemotSnmpd = OIDX_begemotSnmpd,
195 oid_coldStart = OIDX_coldStart,
196 oid_authenticationFailure = OIDX_authenticationFailure;
197
198const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }};
199
200const struct asn_oid oid_usmUnknownEngineIDs =
201 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}};
202
203const struct asn_oid oid_usmNotInTimeWindows =
204 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}};
205
206/* request id generator for traps */
207u_int trap_reqid;
208
209/* help text */
210static const char usgtxt[] = "\
211Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
212Open Communication Systems (FhG Fokus). All rights reserved.\n\
213Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\
214usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\
215 [-l prefix] [-m variable=value] [-p file]\n\
216options:\n\
217 -d don't daemonize\n\
218 -h print this info\n\
219 -c file specify configuration file\n\
220 -D options debugging options\n\
221 -e file specify engine id file\n\
222 -I path system include path\n\
223 -l prefix default basename for pid and config file\n\
224 -m var=val define variable\n\
225 -p file specify pid file\n\
226";
227
228/* hosts_access(3) request */
229#ifdef USE_TCPWRAPPERS
230static struct request_info req;
231#endif
232
233/* transports */
234extern const struct transport_def udp_trans;
235extern const struct transport_def lsock_trans;
236
237struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list);
238
239/* forward declarations */
240static void snmp_printf_func(const char *fmt, ...);
241static void snmp_error_func(const char *err, ...);
242static void snmp_debug_func(const char *err, ...);
243static void asn_error_func(const struct asn_buf *b, const char *err, ...);
244
245/*
246 * Allocate rx/tx buffer. We allocate one byte more for rx.
247 */
248void *
249buf_alloc(int tx)
250{
251 void *buf;
252
253 if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) {
254 syslog(LOG_CRIT, "cannot allocate buffer");
255 if (tx)
256 snmpd_stats.noTxbuf++;
257 else
258 snmpd_stats.noRxbuf++;
259 return (NULL);
260 }
261 return (buf);
262}
263
264/*
265 * Return the buffer size.
266 */
267size_t
268buf_size(int tx)
269{
270 return (tx ? snmpd.txbuf : snmpd.rxbuf);
271}
272
273/*
274 * Prepare a PDU for output
275 */
276void
277snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen,
278 const char *dest)
279{
280 struct asn_buf resp_b;
281
282 resp_b.asn_ptr = sndbuf;
283 resp_b.asn_len = snmpd.txbuf;
284
285 if (snmp_pdu_encode(pdu, &resp_b) != 0) {
286 syslog(LOG_ERR, "cannot encode message");
287 abort();
288 }
289 if (debug.dump_pdus) {
290 snmp_printf("%s <- ", dest);
291 snmp_pdu_dump(pdu);
292 }
293 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
294}
295
296/*
297 * Check USM PDU header credentials against local SNMP Engine & users.
298 */
299static enum snmp_code
300snmp_pdu_auth_user(struct snmp_pdu *pdu)
301{
302 uint64_t etime;
303 usm_user = NULL;
304
305 /* un-authenticated snmpEngineId discovery */
306 if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) {
307 pdu->engine.engine_len = snmpd_engine.engine_len;
308 memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
309 snmpd_engine.engine_len);
310 pdu->engine.engine_boots = snmpd_engine.engine_boots;
311 pdu->engine.engine_time = snmpd_engine.engine_time;
312 pdu->flags |= SNMP_MSG_AUTODISCOVER;
313 return (SNMP_CODE_OK);
314 }
315
316 if ((usm_user = usm_find_user(pdu->engine.engine_id,
317 pdu->engine.engine_len, pdu->user.sec_name)) == NULL ||
318 usm_user->status != 1 /* active */)
319 return (SNMP_CODE_BADUSER);
320
321 if (usm_user->user_engine_len != snmpd_engine.engine_len ||
322 memcmp(usm_user->user_engine_id, snmpd_engine.engine_id,
323 snmpd_engine.engine_len) != 0)
324 return (SNMP_CODE_BADENGINE);
325
326 pdu->user.priv_proto = usm_user->suser.priv_proto;
327 memcpy(pdu->user.priv_key, usm_user->suser.priv_key,
328 sizeof(pdu->user.priv_key));
329
330 /* authenticated snmpEngineId discovery */
331 if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
332 etime = (get_ticks() - start_tick) / 100ULL;
333 if (etime < INT32_MAX)
334 snmpd_engine.engine_time = etime;
335 else {
336 start_tick = get_ticks();
337 set_snmpd_engine();
338 snmpd_engine.engine_time = start_tick;
339 }
340
341 pdu->user.auth_proto = usm_user->suser.auth_proto;
342 memcpy(pdu->user.auth_key, usm_user->suser.auth_key,
343 sizeof(pdu->user.auth_key));
344
345 if (pdu->engine.engine_boots == 0 &&
346 pdu->engine.engine_time == 0) {
347 pdu->flags |= SNMP_MSG_AUTODISCOVER;
348 return (SNMP_CODE_OK);
349 }
350
351 if (pdu->engine.engine_boots != snmpd_engine.engine_boots ||
352 abs(pdu->engine.engine_time - snmpd_engine.engine_time) >
353 SNMP_TIME_WINDOW)
354 return (SNMP_CODE_NOTINTIME);
355 }
356
357 if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 &&
358 (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) ||
359 ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 &&
360 usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) ||
361 ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 &&
362 usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV))
363 return (SNMP_CODE_BADSECLEVEL);
364
365 return (SNMP_CODE_OK);
366}
367
368/*
369 * Check whether access to each of var bindings in the PDU is allowed based
370 * on the user credentials against the configured User groups & VACM views.
371 */
364static enum snmp_code
372enum snmp_code
365snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip)
366{
367 const char *uname;
368 int32_t suboid, smodel;
369 uint32_t i;
370 struct vacm_user *vuser;
371 struct vacm_access *acl;
372 struct vacm_context *vacmctx;
373 struct vacm_view *view;
374
375 /*
376 * At least a default context exists if the snmpd_vacm(3) module is
377 * running.
378 */
379 if (SLIST_EMPTY(&vacm_contextlist) ||
380 (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0)
381 return (SNMP_CODE_OK);
382
383 switch (pdu->version) {
384 case SNMP_V1:
385 if ((uname = comm_string(community)) == NULL)
386 return (SNMP_CODE_FAILED);
387 smodel = SNMP_SECMODEL_SNMPv1;
388 break;
389
390 case SNMP_V2c:
391 if ((uname = comm_string(community)) == NULL)
392 return (SNMP_CODE_FAILED);
393 smodel = SNMP_SECMODEL_SNMPv2c;
394 break;
395
396 case SNMP_V3:
397 uname = pdu->user.sec_name;
398 if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM)
399 return (SNMP_CODE_FAILED);
400 /* Compare the PDU context engine id against the agent's */
401 if (pdu->context_engine_len != snmpd_engine.engine_len ||
402 memcmp(pdu->context_engine, snmpd_engine.engine_id,
403 snmpd_engine.engine_len) != 0)
404 return (SNMP_CODE_FAILED);
405 break;
406
407 default:
408 abort();
409 }
410
411 SLIST_FOREACH(vuser, &vacm_userlist, vvu)
412 if (strcmp(uname, vuser->secname) == 0 &&
413 vuser->sec_model == smodel)
414 break;
415
416 if (vuser == NULL || vuser->group == NULL)
417 return (SNMP_CODE_FAILED);
418
419 /* XXX: shteryana - recheck */
420 TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) {
421 if (acl->group != vuser->group)
422 continue;
423 SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl)
424 if (memcmp(vacmctx->ctxname, acl->ctx_prefix,
425 acl->ctx_match) == 0)
426 goto match;
427 }
428
429 return (SNMP_CODE_FAILED);
430
431match:
432
433 switch (pdu->type) {
434 case SNMP_PDU_GET:
435 case SNMP_PDU_GETNEXT:
436 case SNMP_PDU_GETBULK:
437 if ((view = acl->read_view) == NULL)
438 return (SNMP_CODE_FAILED);
439 break;
440
441 case SNMP_PDU_SET:
442 if ((view = acl->write_view) == NULL)
443 return (SNMP_CODE_FAILED);
444 break;
445
446 case SNMP_PDU_TRAP:
447 case SNMP_PDU_INFORM:
448 case SNMP_PDU_TRAP2:
449 case SNMP_PDU_REPORT:
450 if ((view = acl->notify_view) == NULL)
451 return (SNMP_CODE_FAILED);
452 break;
453 case SNMP_PDU_RESPONSE:
454 /* NOTREACHED */
455 return (SNMP_CODE_FAILED);
456 default:
457 abort();
458 }
459
460 for (i = 0; i < pdu->nbindings; i++) {
461 /* XXX - view->mask*/
462 suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var);
463 if ((!suboid && !view->exclude) || (suboid && view->exclude)) {
464 *ip = i + 1;
465 return (SNMP_CODE_FAILED);
466 }
467 }
468
469 return (SNMP_CODE_OK);
470}
471
472/*
473 * SNMP input. Start: decode the PDU, find the user or community.
474 */
475enum snmpd_input_err
476snmp_input_start(const u_char *buf, size_t len, const char *source,
477 struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen)
478{
479 struct asn_buf b;
480 enum snmp_code code;
481 enum snmpd_input_err ret;
482 int sret;
483
484 /* update uptime */
485 this_tick = get_ticks();
486
487 b.asn_cptr = buf;
488 b.asn_len = len;
489
490 /* look whether we have enough bytes for the entire PDU. */
491 switch (sret = snmp_pdu_snoop(&b)) {
492
493 case 0:
494 return (SNMPD_INPUT_TRUNC);
495
496 case -1:
497 snmpd_stats.inASNParseErrs++;
498 return (SNMPD_INPUT_FAILED);
499 }
500 b.asn_len = *pdulen = (size_t)sret;
501
502 memset(pdu, 0, sizeof(*pdu));
503 if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK)
504 goto decoded;
505
506 if (pdu->version == SNMP_V3) {
507 if (pdu->security_model != SNMP_SECMODEL_USM) {
508 code = SNMP_CODE_FAILED;
509 goto decoded;
510 }
511 if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK)
512 goto decoded;
513 if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK)
514 goto decoded;
515 }
516 code = snmp_pdu_decode_scoped(&b, pdu, ip);
517
518 ret = SNMPD_INPUT_OK;
519
520decoded:
521 snmpd_stats.inPkts++;
522
523 switch (code) {
524
525 case SNMP_CODE_FAILED:
526 snmpd_stats.inASNParseErrs++;
527 return (SNMPD_INPUT_FAILED);
528
529 case SNMP_CODE_BADVERS:
530 bad_vers:
531 snmpd_stats.inBadVersions++;
532 return (SNMPD_INPUT_FAILED);
533
534 case SNMP_CODE_BADLEN:
535 if (pdu->type == SNMP_OP_SET)
536 ret = SNMPD_INPUT_VALBADLEN;
537 break;
538
539 case SNMP_CODE_OORANGE:
540 if (pdu->type == SNMP_OP_SET)
541 ret = SNMPD_INPUT_VALRANGE;
542 break;
543
544 case SNMP_CODE_BADENC:
545 if (pdu->type == SNMP_OP_SET)
546 ret = SNMPD_INPUT_VALBADENC;
547 break;
548
549 case SNMP_CODE_BADSECLEVEL:
550 snmpd_usmstats.unsupported_seclevels++;
551 return (SNMPD_INPUT_FAILED);
552
553 case SNMP_CODE_NOTINTIME:
554 snmpd_usmstats.not_in_time_windows++;
555 return (SNMPD_INPUT_FAILED);
556
557 case SNMP_CODE_BADUSER:
558 snmpd_usmstats.unknown_users++;
559 return (SNMPD_INPUT_FAILED);
560
561 case SNMP_CODE_BADENGINE:
562 snmpd_usmstats.unknown_engine_ids++;
563 return (SNMPD_INPUT_FAILED);
564
565 case SNMP_CODE_BADDIGEST:
566 snmpd_usmstats.wrong_digests++;
567 return (SNMPD_INPUT_FAILED);
568
569 case SNMP_CODE_EDECRYPT:
570 snmpd_usmstats.decrypt_errors++;
571 return (SNMPD_INPUT_FAILED);
572
573 case SNMP_CODE_OK:
574 switch (pdu->version) {
575
576 case SNMP_V1:
577 if (!(snmpd.version_enable & VERS_ENABLE_V1))
578 goto bad_vers;
579 break;
580
581 case SNMP_V2c:
582 if (!(snmpd.version_enable & VERS_ENABLE_V2C))
583 goto bad_vers;
584 break;
585
586 case SNMP_V3:
587 if (!(snmpd.version_enable & VERS_ENABLE_V3))
588 goto bad_vers;
589 break;
590
591 case SNMP_Verr:
592 goto bad_vers;
593 }
594 break;
595 }
596
597 if (debug.dump_pdus) {
598 snmp_printf("%s -> ", source);
599 snmp_pdu_dump(pdu);
600 }
601
602 /*
603 * Look, whether we know the community or user
604 */
605
606 if (pdu->version != SNMP_V3) {
607 TAILQ_FOREACH(comm, &community_list, link)
608 if (comm->string != NULL &&
609 strcmp(comm->string, pdu->community) == 0)
610 break;
611
612 if (comm == NULL) {
613 snmpd_stats.inBadCommunityNames++;
614 snmp_pdu_free(pdu);
615 if (snmpd.auth_traps)
616 snmp_send_trap(&oid_authenticationFailure,
617 (struct snmp_value *)NULL);
618 ret = SNMPD_INPUT_BAD_COMM;
619 } else
620 community = comm->value;
621 } else if (pdu->nbindings == 0) {
622 /* RFC 3414 - snmpEngineID Discovery */
623 if (strlen(pdu->user.sec_name) == 0) {
624 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
625 &oid_usmUnknownEngineIDs);
626 pdu->context_engine_len = snmpd_engine.engine_len;
627 memcpy(pdu->context_engine, snmpd_engine.engine_id,
628 snmpd_engine.engine_len);
629 } else if (pdu->engine.engine_boots == 0 &&
630 pdu->engine.engine_time == 0) {
631 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
632 &oid_usmNotInTimeWindows);
633 pdu->engine.engine_boots = snmpd_engine.engine_boots;
634 pdu->engine.engine_time = snmpd_engine.engine_time;
635 }
636 } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH &&
637 (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) {
638 snmpd_usmstats.not_in_time_windows++;
639 ret = SNMP_CODE_FAILED;
640 }
641
642 if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK)
643 ret = SNMP_CODE_FAILED;
644
645 return (ret);
646}
647
648/*
649 * Will return only _OK or _FAILED
650 */
651enum snmpd_input_err
652snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen,
653 u_char *sndbuf, size_t *sndlen, const char *source,
654 enum snmpd_input_err ierr, int32_t ivar, void *data)
655{
656 struct snmp_pdu resp;
657 struct asn_buf resp_b, pdu_b;
658 enum snmp_ret ret;
659
660 resp_b.asn_ptr = sndbuf;
661 resp_b.asn_len = snmpd.txbuf;
662
663 pdu_b.asn_cptr = rcvbuf;
664 pdu_b.asn_len = rcvlen;
665
666 if (ierr != SNMPD_INPUT_OK) {
667 /* error decoding the input of a SET */
668 if (pdu->version == SNMP_V1)
669 pdu->error_status = SNMP_ERR_BADVALUE;
670 else if (ierr == SNMPD_INPUT_VALBADLEN)
671 pdu->error_status = SNMP_ERR_WRONG_LENGTH;
672 else if (ierr == SNMPD_INPUT_VALRANGE)
673 pdu->error_status = SNMP_ERR_WRONG_VALUE;
674 else
675 pdu->error_status = SNMP_ERR_WRONG_ENCODING;
676
677 pdu->error_index = ivar;
678
679 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
680 syslog(LOG_WARNING, "could not encode error response");
681 snmpd_stats.silentDrops++;
682 return (SNMPD_INPUT_FAILED);
683 }
684
685 if (debug.dump_pdus) {
686 snmp_printf("%s <- ", source);
687 snmp_pdu_dump(pdu);
688 }
689 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
690 return (SNMPD_INPUT_OK);
691 }
692
693 switch (pdu->type) {
694
695 case SNMP_PDU_GET:
696 ret = snmp_get(pdu, &resp_b, &resp, data);
697 break;
698
699 case SNMP_PDU_GETNEXT:
700 ret = snmp_getnext(pdu, &resp_b, &resp, data);
701 break;
702
703 case SNMP_PDU_SET:
704 ret = snmp_set(pdu, &resp_b, &resp, data);
705 break;
706
707 case SNMP_PDU_GETBULK:
708 ret = snmp_getbulk(pdu, &resp_b, &resp, data);
709 break;
710
711 default:
712 ret = SNMP_RET_IGN;
713 break;
714 }
715
716 switch (ret) {
717
718 case SNMP_RET_OK:
719 /* normal return - send a response */
720 if (debug.dump_pdus) {
721 snmp_printf("%s <- ", source);
722 snmp_pdu_dump(&resp);
723 }
724 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
725 snmp_pdu_free(&resp);
726 return (SNMPD_INPUT_OK);
727
728 case SNMP_RET_IGN:
729 /* error - send nothing */
730 snmpd_stats.silentDrops++;
731 return (SNMPD_INPUT_FAILED);
732
733 case SNMP_RET_ERR:
734 /* error - send error response. The snmp routine has
735 * changed the error fields in the original message. */
736 resp_b.asn_ptr = sndbuf;
737 resp_b.asn_len = snmpd.txbuf;
738 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
739 syslog(LOG_WARNING, "could not encode error response");
740 snmpd_stats.silentDrops++;
741 return (SNMPD_INPUT_FAILED);
742 } else {
743 if (debug.dump_pdus) {
744 snmp_printf("%s <- ", source);
745 snmp_pdu_dump(pdu);
746 }
747 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
748 return (SNMPD_INPUT_OK);
749 }
750 }
751 abort();
752}
753
754/*
755 * Insert a port into the right place in the transport's table of ports
756 */
757void
758trans_insert_port(struct transport *t, struct tport *port)
759{
760 struct tport *p;
761
762 TAILQ_FOREACH(p, &t->table, link) {
763 if (asn_compare_oid(&p->index, &port->index) > 0) {
764 TAILQ_INSERT_BEFORE(p, port, link);
765 return;
766 }
767 }
768 port->transport = t;
769 TAILQ_INSERT_TAIL(&t->table, port, link);
770}
771
772/*
773 * Remove a port from a transport's list
774 */
775void
776trans_remove_port(struct tport *port)
777{
778
779 TAILQ_REMOVE(&port->transport->table, port, link);
780}
781
782/*
783 * Find a port on a transport's list
784 */
785struct tport *
786trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub)
787{
788
789 return (FIND_OBJECT_OID(&t->table, idx, sub));
790}
791
792/*
793 * Find next port on a transport's list
794 */
795struct tport *
796trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub)
797{
798
799 return (NEXT_OBJECT_OID(&t->table, idx, sub));
800}
801
802/*
803 * Return first port
804 */
805struct tport *
806trans_first_port(struct transport *t)
807{
808
809 return (TAILQ_FIRST(&t->table));
810}
811
812/*
813 * Iterate through all ports until a function returns a 0.
814 */
815struct tport *
816trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t),
817 intptr_t arg)
818{
819 struct tport *p;
820
821 TAILQ_FOREACH(p, &t->table, link)
822 if (func(p, arg) == 0)
823 return (p);
824 return (NULL);
825}
826
827/*
828 * Register a transport
829 */
830int
831trans_register(const struct transport_def *def, struct transport **pp)
832{
833 u_int i;
834 char or_descr[256];
835
836 if ((*pp = malloc(sizeof(**pp))) == NULL)
837 return (SNMP_ERR_GENERR);
838
839 /* construct index */
840 (*pp)->index.len = strlen(def->name) + 1;
841 (*pp)->index.subs[0] = strlen(def->name);
842 for (i = 0; i < (*pp)->index.subs[0]; i++)
843 (*pp)->index.subs[i + 1] = def->name[i];
844
845 (*pp)->vtab = def;
846
847 if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) {
848 free(*pp);
849 return (SNMP_ERR_INCONS_VALUE);
850 }
851
852 /* register module */
853 snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name);
854 if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) {
855 free(*pp);
856 return (SNMP_ERR_GENERR);
857 }
858
859 INSERT_OBJECT_OID((*pp), &transport_list);
860
861 TAILQ_INIT(&(*pp)->table);
862
863 return (SNMP_ERR_NOERROR);
864}
865
866/*
867 * Unregister transport
868 */
869int
870trans_unregister(struct transport *t)
871{
872 if (!TAILQ_EMPTY(&t->table))
873 return (SNMP_ERR_INCONS_VALUE);
874
875 or_unregister(t->or_index);
876 TAILQ_REMOVE(&transport_list, t, link);
877
878 return (SNMP_ERR_NOERROR);
879}
880
881/*
882 * File descriptor support
883 */
884#ifdef USE_LIBBEGEMOT
885static void
886input(int fd, int mask __unused, void *uap)
887#else
888static void
889input(evContext ctx __unused, void *uap, int fd, int mask __unused)
890#endif
891{
892 struct fdesc *f = uap;
893
894 (*f->func)(fd, f->udata);
895}
896
897void
898fd_suspend(void *p)
899{
900 struct fdesc *f = p;
901
902#ifdef USE_LIBBEGEMOT
903 if (f->id >= 0) {
904 poll_unregister(f->id);
905 f->id = -1;
906 }
907#else
908 if (evTestID(f->id)) {
909 (void)evDeselectFD(evctx, f->id);
910 evInitID(&f->id);
911 }
912#endif
913}
914
915int
916fd_resume(void *p)
917{
918 struct fdesc *f = p;
919 int err;
920
921#ifdef USE_LIBBEGEMOT
922 if (f->id >= 0)
923 return (0);
924 if ((f->id = poll_register(f->fd, input, f, POLL_IN)) < 0) {
925 err = errno;
926 syslog(LOG_ERR, "select fd %d: %m", f->fd);
927 errno = err;
928 return (-1);
929 }
930#else
931 if (evTestID(f->id))
932 return (0);
933 if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) {
934 err = errno;
935 syslog(LOG_ERR, "select fd %d: %m", f->fd);
936 errno = err;
937 return (-1);
938 }
939#endif
940 return (0);
941}
942
943void *
944fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod)
945{
946 struct fdesc *f;
947 int err;
948
949 if ((f = malloc(sizeof(struct fdesc))) == NULL) {
950 err = errno;
951 syslog(LOG_ERR, "fd_select: %m");
952 errno = err;
953 return (NULL);
954 }
955 f->fd = fd;
956 f->func = func;
957 f->udata = udata;
958 f->owner = mod;
959#ifdef USE_LIBBEGEMOT
960 f->id = -1;
961#else
962 evInitID(&f->id);
963#endif
964
965 if (fd_resume(f)) {
966 err = errno;
967 free(f);
968 errno = err;
969 return (NULL);
970 }
971
972 LIST_INSERT_HEAD(&fdesc_list, f, link);
973
974 return (f);
975}
976
977void
978fd_deselect(void *p)
979{
980 struct fdesc *f = p;
981
982 LIST_REMOVE(f, link);
983 fd_suspend(f);
984 free(f);
985}
986
987static void
988fd_flush(struct lmodule *mod)
989{
990 struct fdesc *t, *t1;
991
992 t = LIST_FIRST(&fdesc_list);
993 while (t != NULL) {
994 t1 = LIST_NEXT(t, link);
995 if (t->owner == mod)
996 fd_deselect(t);
997 t = t1;
998 }
999}
1000
1001/*
1002 * Consume a message from the input buffer
1003 */
1004static void
1005snmp_input_consume(struct port_input *pi)
1006{
1007 if (!pi->stream) {
1008 /* always consume everything */
1009 pi->length = 0;
1010 return;
1011 }
1012 if (pi->consumed >= pi->length) {
1013 /* all bytes consumed */
1014 pi->length = 0;
1015 return;
1016 }
1017 memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed);
1018 pi->length -= pi->consumed;
1019}
1020
1021struct credmsg {
1022 struct cmsghdr hdr;
1023 struct cmsgcred cred;
1024};
1025
1026static void
1027check_priv(struct port_input *pi, struct msghdr *msg)
1028{
1029 struct credmsg *cmsg;
1030 struct xucred ucred;
1031 socklen_t ucredlen;
1032
1033 pi->priv = 0;
1034
1035 if (msg->msg_controllen == sizeof(*cmsg)) {
1036 /* process explicitly sends credentials */
1037
1038 cmsg = (struct credmsg *)msg->msg_control;
1039 pi->priv = (cmsg->cred.cmcred_euid == 0);
1040 return;
1041 }
1042
1043 /* ok, obtain the accept time credentials */
1044 ucredlen = sizeof(ucred);
1045
1046 if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
1047 ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
1048 pi->priv = (ucred.cr_uid == 0);
1049}
1050
1051/*
1052 * Input from a stream socket.
1053 */
1054static int
1055recv_stream(struct port_input *pi)
1056{
1057 struct msghdr msg;
1058 struct iovec iov[1];
1059 ssize_t len;
1060 struct credmsg cmsg;
1061
1062 if (pi->buf == NULL) {
1063 /* no buffer yet - allocate one */
1064 if ((pi->buf = buf_alloc(0)) == NULL) {
1065 /* ups - could not get buffer. Return an error
1066 * the caller must close the transport. */
1067 return (-1);
1068 }
1069 pi->buflen = buf_size(0);
1070 pi->consumed = 0;
1071 pi->length = 0;
1072 }
1073
1074 /* try to get a message */
1075 msg.msg_name = pi->peer;
1076 msg.msg_namelen = pi->peerlen;
1077 msg.msg_iov = iov;
1078 msg.msg_iovlen = 1;
1079 if (pi->cred) {
1080 msg.msg_control = &cmsg;
1081 msg.msg_controllen = sizeof(cmsg);
1082
1083 cmsg.hdr.cmsg_len = sizeof(cmsg);
1084 cmsg.hdr.cmsg_level = SOL_SOCKET;
1085 cmsg.hdr.cmsg_type = SCM_CREDS;
1086 } else {
1087 msg.msg_control = NULL;
1088 msg.msg_controllen = 0;
1089 }
1090 msg.msg_flags = 0;
1091
1092 iov[0].iov_base = pi->buf + pi->length;
1093 iov[0].iov_len = pi->buflen - pi->length;
1094
1095 len = recvmsg(pi->fd, &msg, 0);
1096
1097 if (len == -1 || len == 0)
1098 /* receive error */
1099 return (-1);
1100
1101 pi->length += len;
1102
1103 if (pi->cred)
1104 check_priv(pi, &msg);
1105
1106 return (0);
1107}
1108
1109/*
1110 * Input from a datagram socket.
1111 * Each receive should return one datagram.
1112 */
1113static int
1114recv_dgram(struct port_input *pi)
1115{
1116 u_char embuf[1000];
1117 struct msghdr msg;
1118 struct iovec iov[1];
1119 ssize_t len;
1120 struct credmsg cmsg;
1121
1122 if (pi->buf == NULL) {
1123 /* no buffer yet - allocate one */
1124 if ((pi->buf = buf_alloc(0)) == NULL) {
1125 /* ups - could not get buffer. Read away input
1126 * and drop it */
1127 (void)recvfrom(pi->fd, embuf, sizeof(embuf),
1128 0, NULL, NULL);
1129 /* return error */
1130 return (-1);
1131 }
1132 pi->buflen = buf_size(0);
1133 }
1134
1135 /* try to get a message */
1136 msg.msg_name = pi->peer;
1137 msg.msg_namelen = pi->peerlen;
1138 msg.msg_iov = iov;
1139 msg.msg_iovlen = 1;
1140 if (pi->cred) {
1141 msg.msg_control = &cmsg;
1142 msg.msg_controllen = sizeof(cmsg);
1143
1144 cmsg.hdr.cmsg_len = sizeof(cmsg);
1145 cmsg.hdr.cmsg_level = SOL_SOCKET;
1146 cmsg.hdr.cmsg_type = SCM_CREDS;
1147 } else {
1148 msg.msg_control = NULL;
1149 msg.msg_controllen = 0;
1150 }
1151 msg.msg_flags = 0;
1152
1153 iov[0].iov_base = pi->buf;
1154 iov[0].iov_len = pi->buflen;
1155
1156 len = recvmsg(pi->fd, &msg, 0);
1157
1158 if (len == -1 || len == 0)
1159 /* receive error */
1160 return (-1);
1161
1162 if (msg.msg_flags & MSG_TRUNC) {
1163 /* truncated - drop */
1164 snmpd_stats.silentDrops++;
1165 snmpd_stats.inTooLong++;
1166 return (-1);
1167 }
1168
1169 pi->length = (size_t)len;
1170
1171 if (pi->cred)
1172 check_priv(pi, &msg);
1173
1174 return (0);
1175}
1176
1177/*
1178 * Input from a socket
1179 */
1180int
1181snmpd_input(struct port_input *pi, struct tport *tport)
1182{
1183 u_char *sndbuf;
1184 size_t sndlen;
1185 struct snmp_pdu pdu;
1186 enum snmpd_input_err ierr, ferr;
1187 enum snmpd_proxy_err perr;
1188 int32_t vi;
1189 int ret;
1190 ssize_t slen;
1191#ifdef USE_TCPWRAPPERS
1192 char client[16];
1193#endif
1194
1195 /* get input depending on the transport */
1196 if (pi->stream) {
1197 ret = recv_stream(pi);
1198 } else {
1199 ret = recv_dgram(pi);
1200 }
1201
1202 if (ret == -1)
1203 return (-1);
1204
1205#ifdef USE_TCPWRAPPERS
1206 /*
1207 * In case of AF_INET{6} peer, do hosts_access(5) check.
1208 */
1209 if (inet_ntop(pi->peer->sa_family,
1210 &((const struct sockaddr_in *)(const void *)pi->peer)->sin_addr,
1211 client, sizeof(client)) != NULL) {
1212 request_set(&req, RQ_CLIENT_ADDR, client, 0);
1213 if (hosts_access(&req) == 0) {
1214 syslog(LOG_ERR, "refused connection from %.500s",
1215 eval_client(&req));
1216 return (-1);
1217 }
1218 } else
1219 syslog(LOG_ERR, "inet_ntop(): %m");
1220#endif
1221
1222 /*
1223 * Handle input
1224 */
1225 ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi,
1226 &pi->consumed);
1227 if (ierr == SNMPD_INPUT_TRUNC) {
1228 /* need more bytes. This is ok only for streaming transports.
1229 * but only if we have not reached bufsiz yet. */
1230 if (pi->stream) {
1231 if (pi->length == buf_size(0)) {
1232 snmpd_stats.silentDrops++;
1233 return (-1);
1234 }
1235 return (0);
1236 }
1237 snmpd_stats.silentDrops++;
1238 return (-1);
1239 }
1240
1241 /* can't check for bad SET pdus here, because a proxy may have to
1242 * check the access first. We don't want to return an error response
1243 * to a proxy PDU with a wrong community */
1244 if (ierr == SNMPD_INPUT_FAILED) {
1245 /* for streaming transports this is fatal */
1246 if (pi->stream)
1247 return (-1);
1248 snmp_input_consume(pi);
1249 return (0);
1250 }
1251 if (ierr == SNMPD_INPUT_BAD_COMM) {
1252 snmp_input_consume(pi);
1253 return (0);
1254 }
1255
1256 /*
1257 * If that is a module community and the module has a proxy function,
1258 * the hand it over to the module.
1259 */
1260 if (comm != NULL && comm->owner != NULL &&
1261 comm->owner->config->proxy != NULL) {
1262 perr = (*comm->owner->config->proxy)(&pdu, tport->transport,
1263 &tport->index, pi->peer, pi->peerlen, ierr, vi,
1264 !pi->cred || pi->priv);
1265
1266 switch (perr) {
1267
1268 case SNMPD_PROXY_OK:
1269 snmp_input_consume(pi);
1270 return (0);
1271
1272 case SNMPD_PROXY_REJ:
1273 break;
1274
1275 case SNMPD_PROXY_DROP:
1276 snmp_input_consume(pi);
1277 snmp_pdu_free(&pdu);
1278 snmpd_stats.proxyDrops++;
1279 return (0);
1280
1281 case SNMPD_PROXY_BADCOMM:
1282 snmp_input_consume(pi);
1283 snmp_pdu_free(&pdu);
1284 snmpd_stats.inBadCommunityNames++;
1285 if (snmpd.auth_traps)
1286 snmp_send_trap(&oid_authenticationFailure,
1287 (struct snmp_value *)NULL);
1288 return (0);
1289
1290 case SNMPD_PROXY_BADCOMMUSE:
1291 snmp_input_consume(pi);
1292 snmp_pdu_free(&pdu);
1293 snmpd_stats.inBadCommunityUses++;
1294 if (snmpd.auth_traps)
1295 snmp_send_trap(&oid_authenticationFailure,
1296 (struct snmp_value *)NULL);
1297 return (0);
1298 }
1299 }
1300
1301 /*
1302 * Check type
1303 */
1304 if (pdu.type == SNMP_PDU_RESPONSE ||
1305 pdu.type == SNMP_PDU_TRAP ||
1306 pdu.type == SNMP_PDU_TRAP2) {
1307 snmpd_stats.silentDrops++;
1308 snmpd_stats.inBadPduTypes++;
1309 snmp_pdu_free(&pdu);
1310 snmp_input_consume(pi);
1311 return (0);
1312 }
1313
1314 /*
1315 * Check community
1316 */
1317 if (pdu.version < SNMP_V3 &&
1318 ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) ||
1319 (community != COMM_WRITE &&
1320 (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) {
1321 snmpd_stats.inBadCommunityUses++;
1322 snmp_pdu_free(&pdu);
1323 snmp_input_consume(pi);
1324 if (snmpd.auth_traps)
1325 snmp_send_trap(&oid_authenticationFailure,
1326 (struct snmp_value *)NULL);
1327 return (0);
1328 }
1329
1330 /*
1331 * Execute it.
1332 */
1333 if ((sndbuf = buf_alloc(1)) == NULL) {
1334 snmpd_stats.silentDrops++;
1335 snmp_pdu_free(&pdu);
1336 snmp_input_consume(pi);
1337 return (0);
1338 }
1339 ferr = snmp_input_finish(&pdu, pi->buf, pi->length,
1340 sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
1341
1342 if (ferr == SNMPD_INPUT_OK) {
1343 slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen);
1344 if (slen == -1)
1345 syslog(LOG_ERR, "sendto: %m");
1346 else if ((size_t)slen != sndlen)
1347 syslog(LOG_ERR, "sendto: short write %zu/%zu",
1348 sndlen, (size_t)slen);
1349 }
1350 snmp_pdu_free(&pdu);
1351 free(sndbuf);
1352 snmp_input_consume(pi);
1353
1354 return (0);
1355}
1356
1357/*
1358 * Send a PDU to a given port
1359 */
1360void
1361snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu,
1362 const struct sockaddr *addr, socklen_t addrlen)
1363{
1364 struct transport *trans = targ;
1365 struct tport *tp;
1366 u_char *sndbuf;
1367 size_t sndlen;
1368 ssize_t len;
1369
1370 TAILQ_FOREACH(tp, &trans->table, link)
1371 if (asn_compare_oid(port, &tp->index) == 0)
1372 break;
1373 if (tp == 0)
1374 return;
1375
1376 if ((sndbuf = buf_alloc(1)) == NULL)
1377 return;
1378
1379 snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY");
1380
1381 len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen);
1382
1383 if (len == -1)
1384 syslog(LOG_ERR, "sendto: %m");
1385 else if ((size_t)len != sndlen)
1386 syslog(LOG_ERR, "sendto: short write %zu/%zu",
1387 sndlen, (size_t)len);
1388
1389 free(sndbuf);
1390}
1391
1392
1393/*
1394 * Close an input source
1395 */
1396void
1397snmpd_input_close(struct port_input *pi)
1398{
1399 if (pi->id != NULL)
1400 fd_deselect(pi->id);
1401 if (pi->fd >= 0)
1402 (void)close(pi->fd);
1403 if (pi->buf != NULL)
1404 free(pi->buf);
1405}
1406
1407/*
1408 * Dump internal state.
1409 */
1410#ifdef USE_LIBBEGEMOT
1411static void
1412info_func(void)
1413#else
1414static void
1415info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused)
1416#endif
1417{
1418 struct lmodule *m;
1419 u_int i;
1420 char buf[10000];
1421
1422 syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid());
1423 for (i = 0; i < tree_size; i++) {
1424 switch (tree[i].type) {
1425
1426 case SNMP_NODE_LEAF:
1427 sprintf(buf, "LEAF: %s %s", tree[i].name,
1428 asn_oid2str(&tree[i].oid));
1429 break;
1430
1431 case SNMP_NODE_COLUMN:
1432 sprintf(buf, "COL: %s %s", tree[i].name,
1433 asn_oid2str(&tree[i].oid));
1434 break;
1435 }
1436 syslog(LOG_DEBUG, "%s", buf);
1437 }
1438
1439 TAILQ_FOREACH(m, &lmodules, link)
1440 if (m->config->dump)
1441 (*m->config->dump)();
1442}
1443
1444/*
1445 * Re-read configuration
1446 */
1447#ifdef USE_LIBBEGEMOT
1448static void
1449config_func(void)
1450#else
1451static void
1452config_func(evContext ctx __unused, void *uap __unused,
1453 const void *tag __unused)
1454#endif
1455{
1456 struct lmodule *m;
1457
1458 if (read_config(config_file, NULL)) {
1459 syslog(LOG_ERR, "error reading config file '%s'", config_file);
1460 return;
1461 }
1462 TAILQ_FOREACH(m, &lmodules, link)
1463 if (m->config->config)
1464 (*m->config->config)();
1465}
1466
1467/*
1468 * On USR1 dump actual configuration.
1469 */
1470static void
1471onusr1(int s __unused)
1472{
1473
1474 work |= WORK_DOINFO;
1475}
1476static void
1477onhup(int s __unused)
1478{
1479
1480 work |= WORK_RECONFIG;
1481}
1482
1483static void
1484onterm(int s __unused)
1485{
1486
1487 /* allow clean-up */
1488 exit(0);
1489}
1490
1491static void
1492init_sigs(void)
1493{
1494 struct sigaction sa;
1495
1496 sa.sa_handler = onusr1;
1497 sa.sa_flags = SA_RESTART;
1498 sigemptyset(&sa.sa_mask);
1499 if (sigaction(SIGUSR1, &sa, NULL)) {
1500 syslog(LOG_ERR, "sigaction: %m");
1501 exit(1);
1502 }
1503
1504 sa.sa_handler = onhup;
1505 if (sigaction(SIGHUP, &sa, NULL)) {
1506 syslog(LOG_ERR, "sigaction: %m");
1507 exit(1);
1508 }
1509
1510 sa.sa_handler = onterm;
1511 sa.sa_flags = 0;
1512 sigemptyset(&sa.sa_mask);
1513 if (sigaction(SIGTERM, &sa, NULL)) {
1514 syslog(LOG_ERR, "sigaction: %m");
1515 exit(1);
1516 }
1517 if (sigaction(SIGINT, &sa, NULL)) {
1518 syslog(LOG_ERR, "sigaction: %m");
1519 exit(1);
1520 }
1521}
1522
1523static void
1524block_sigs(void)
1525{
1526 sigset_t set;
1527
1528 sigfillset(&set);
1529 if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) {
1530 syslog(LOG_ERR, "SIG_BLOCK: %m");
1531 exit(1);
1532 }
1533}
1534static void
1535unblock_sigs(void)
1536{
1537 if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) {
1538 syslog(LOG_ERR, "SIG_SETMASK: %m");
1539 exit(1);
1540 }
1541}
1542
1543/*
1544 * Shut down
1545 */
1546static void
1547term(void)
1548{
1549 (void)unlink(pid_file);
1550}
1551
1552static void
1553trans_stop(void)
1554{
1555 struct transport *t;
1556
1557 TAILQ_FOREACH(t, &transport_list, link)
1558 (void)t->vtab->stop(1);
1559}
1560
1561/*
1562 * Define a macro from the command line
1563 */
1564static void
1565do_macro(char *arg)
1566{
1567 char *eq;
1568 int err;
1569
1570 if ((eq = strchr(arg, '=')) == NULL)
1571 err = define_macro(arg, "");
1572 else {
1573 *eq++ = '\0';
1574 err = define_macro(arg, eq);
1575 }
1576 if (err == -1) {
1577 syslog(LOG_ERR, "cannot save macro: %m");
1578 exit(1);
1579 }
1580}
1581
1582/*
1583 * Re-implement getsubopt from scratch, because the second argument is broken
1584 * and will not compile with WARNS=5.
1585 */
1586static int
1587getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
1588{
1589 static const char *const delim = ",\t ";
1590 u_int i;
1591 char *ptr;
1592
1593 *optp = NULL;
1594
1595 /* skip leading junk */
1596 for (ptr = *arg; *ptr != '\0'; ptr++)
1597 if (strchr(delim, *ptr) == NULL)
1598 break;
1599 if (*ptr == '\0') {
1600 *arg = ptr;
1601 return (-1);
1602 }
1603 *optp = ptr;
1604
1605 /* find the end of the option */
1606 while (*++ptr != '\0')
1607 if (strchr(delim, *ptr) != NULL || *ptr == '=')
1608 break;
1609
1610 if (*ptr != '\0') {
1611 if (*ptr == '=') {
1612 *ptr++ = '\0';
1613 *valp = ptr;
1614 while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
1615 ptr++;
1616 if (*ptr != '\0')
1617 *ptr++ = '\0';
1618 } else
1619 *ptr++ = '\0';
1620 }
1621
1622 *arg = ptr;
1623
1624 for (i = 0; *options != NULL; options++, i++)
1625 if (strcmp(*optp, *options) == 0)
1626 return (i);
1627 return (-1);
1628}
1629
1630int
1631main(int argc, char *argv[])
1632{
1633 int opt;
1634 FILE *fp;
1635 int background = 1;
1636 struct tport *p;
1637 const char *prefix = "snmpd";
1638 struct lmodule *m;
1639 char *value = NULL, *option; /* XXX */
1640 struct transport *t;
1641
1642#define DBG_DUMP 0
1643#define DBG_EVENTS 1
1644#define DBG_TRACE 2
1645 static const char *const debug_opts[] = {
1646 "dump",
1647 "events",
1648 "trace",
1649 NULL
1650 };
1651
1652 snmp_printf = snmp_printf_func;
1653 snmp_error = snmp_error_func;
1654 snmp_debug = snmp_debug_func;
1655 asn_error = asn_error_func;
1656
1657 while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF)
1658 switch (opt) {
1659
1660 case 'c':
1661 strlcpy(config_file, optarg, sizeof(config_file));
1662 break;
1663
1664 case 'd':
1665 background = 0;
1666 break;
1667
1668 case 'D':
1669 while (*optarg) {
1670 switch (getsubopt1(&optarg, debug_opts,
1671 &value, &option)) {
1672
1673 case DBG_DUMP:
1674 debug.dump_pdus = 1;
1675 break;
1676
1677 case DBG_EVENTS:
1678 debug.evdebug++;
1679 break;
1680
1681 case DBG_TRACE:
1682 if (value == NULL)
1683 syslog(LOG_ERR,
1684 "no value for 'trace'");
1685 else
1686 snmp_trace = strtoul(value,
1687 NULL, 0);
1688 break;
1689
1690 case -1:
1691 if (suboptarg)
1692 syslog(LOG_ERR,
1693 "unknown debug flag '%s'",
1694 option);
1695 else
1696 syslog(LOG_ERR,
1697 "missing debug flag");
1698 break;
1699 }
1700 }
1701 break;
1702
1703 case 'e':
1704 strlcpy(engine_file, optarg, sizeof(engine_file));
1705 break;
1706 case 'h':
1707 fprintf(stderr, "%s", usgtxt);
1708 exit(0);
1709
1710 case 'I':
1711 syspath = optarg;
1712 break;
1713
1714 case 'l':
1715 prefix = optarg;
1716 break;
1717
1718 case 'm':
1719 do_macro(optarg);
1720 break;
1721
1722 case 'p':
1723 strlcpy(pid_file, optarg, sizeof(pid_file));
1724 break;
1725 }
1726
1727 openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER);
1728 setlogmask(LOG_UPTO(debug.logpri - 1));
1729
1730 if (background && daemon(0, 0) < 0) {
1731 syslog(LOG_ERR, "daemon: %m");
1732 exit(1);
1733 }
1734
1735 argc -= optind;
1736 argv += optind;
1737
1738 progargs = argv;
1739 nprogargs = argc;
1740
1741 srandomdev();
1742
1743 snmp_serial_no = random();
1744
1745#ifdef USE_TCPWRAPPERS
1746 /*
1747 * Initialize hosts_access(3) handler.
1748 */
1749 request_init(&req, RQ_DAEMON, "snmpd", 0);
1750 sock_methods(&req);
1751#endif
1752
1753 /*
1754 * Initialize the tree.
1755 */
1756 if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) {
1757 syslog(LOG_ERR, "%m");
1758 exit(1);
1759 }
1760 memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE);
1761 tree_size = CTREE_SIZE;
1762
1763 /*
1764 * Get standard communities
1765 */
1766 (void)comm_define(1, "SNMP read", NULL, NULL);
1767 (void)comm_define(2, "SNMP write", NULL, NULL);
1768 community = COMM_INITIALIZE;
1769
1770 trap_reqid = reqid_allocate(512, NULL);
1771
1772 if (config_file[0] == '\0')
1773 snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix);
1774
1775 init_actvals();
1776 init_snmpd_engine();
1777
1778 this_tick = get_ticks();
1779 start_tick = this_tick;
1780
1781 /* start transports */
1782 if (atexit(trans_stop) == -1) {
1783 syslog(LOG_ERR, "atexit failed: %m");
1784 exit(1);
1785 }
1786 if (udp_trans.start() != SNMP_ERR_NOERROR)
1787 syslog(LOG_WARNING, "cannot start UDP transport");
1788 if (lsock_trans.start() != SNMP_ERR_NOERROR)
1789 syslog(LOG_WARNING, "cannot start LSOCK transport");
1790
1791#ifdef USE_LIBBEGEMOT
1792 if (debug.evdebug > 0)
1793 rpoll_trace = 1;
1794#else
1795 if (evCreate(&evctx)) {
1796 syslog(LOG_ERR, "evCreate: %m");
1797 exit(1);
1798 }
1799 if (debug.evdebug > 0)
1800 evSetDebug(evctx, 10, stderr);
1801#endif
1802
1803 if (engine_file[0] == '\0')
1804 snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix);
1805
1806 if (read_config(config_file, NULL)) {
1807 syslog(LOG_ERR, "error in config file");
1808 exit(1);
1809 }
1810
1811 TAILQ_FOREACH(t, &transport_list, link)
1812 TAILQ_FOREACH(p, &t->table, link)
1813 t->vtab->init_port(p);
1814
1815 init_sigs();
1816
1817 if (pid_file[0] == '\0')
1818 snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix);
1819
1820 if ((fp = fopen(pid_file, "w")) != NULL) {
1821 fprintf(fp, "%u", getpid());
1822 fclose(fp);
1823 if (atexit(term) == -1) {
1824 syslog(LOG_ERR, "atexit failed: %m");
1825 (void)remove(pid_file);
1826 exit(0);
1827 }
1828 }
1829
1830 if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.",
1831 NULL) == 0) {
1832 syslog(LOG_ERR, "cannot register SNMPv2 MIB");
1833 exit(1);
1834 }
1835 if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.",
1836 NULL) == 0) {
1837 syslog(LOG_ERR, "cannot register begemotSnmpd MIB");
1838 exit(1);
1839 }
1840
373snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip)
374{
375 const char *uname;
376 int32_t suboid, smodel;
377 uint32_t i;
378 struct vacm_user *vuser;
379 struct vacm_access *acl;
380 struct vacm_context *vacmctx;
381 struct vacm_view *view;
382
383 /*
384 * At least a default context exists if the snmpd_vacm(3) module is
385 * running.
386 */
387 if (SLIST_EMPTY(&vacm_contextlist) ||
388 (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0)
389 return (SNMP_CODE_OK);
390
391 switch (pdu->version) {
392 case SNMP_V1:
393 if ((uname = comm_string(community)) == NULL)
394 return (SNMP_CODE_FAILED);
395 smodel = SNMP_SECMODEL_SNMPv1;
396 break;
397
398 case SNMP_V2c:
399 if ((uname = comm_string(community)) == NULL)
400 return (SNMP_CODE_FAILED);
401 smodel = SNMP_SECMODEL_SNMPv2c;
402 break;
403
404 case SNMP_V3:
405 uname = pdu->user.sec_name;
406 if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM)
407 return (SNMP_CODE_FAILED);
408 /* Compare the PDU context engine id against the agent's */
409 if (pdu->context_engine_len != snmpd_engine.engine_len ||
410 memcmp(pdu->context_engine, snmpd_engine.engine_id,
411 snmpd_engine.engine_len) != 0)
412 return (SNMP_CODE_FAILED);
413 break;
414
415 default:
416 abort();
417 }
418
419 SLIST_FOREACH(vuser, &vacm_userlist, vvu)
420 if (strcmp(uname, vuser->secname) == 0 &&
421 vuser->sec_model == smodel)
422 break;
423
424 if (vuser == NULL || vuser->group == NULL)
425 return (SNMP_CODE_FAILED);
426
427 /* XXX: shteryana - recheck */
428 TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) {
429 if (acl->group != vuser->group)
430 continue;
431 SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl)
432 if (memcmp(vacmctx->ctxname, acl->ctx_prefix,
433 acl->ctx_match) == 0)
434 goto match;
435 }
436
437 return (SNMP_CODE_FAILED);
438
439match:
440
441 switch (pdu->type) {
442 case SNMP_PDU_GET:
443 case SNMP_PDU_GETNEXT:
444 case SNMP_PDU_GETBULK:
445 if ((view = acl->read_view) == NULL)
446 return (SNMP_CODE_FAILED);
447 break;
448
449 case SNMP_PDU_SET:
450 if ((view = acl->write_view) == NULL)
451 return (SNMP_CODE_FAILED);
452 break;
453
454 case SNMP_PDU_TRAP:
455 case SNMP_PDU_INFORM:
456 case SNMP_PDU_TRAP2:
457 case SNMP_PDU_REPORT:
458 if ((view = acl->notify_view) == NULL)
459 return (SNMP_CODE_FAILED);
460 break;
461 case SNMP_PDU_RESPONSE:
462 /* NOTREACHED */
463 return (SNMP_CODE_FAILED);
464 default:
465 abort();
466 }
467
468 for (i = 0; i < pdu->nbindings; i++) {
469 /* XXX - view->mask*/
470 suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var);
471 if ((!suboid && !view->exclude) || (suboid && view->exclude)) {
472 *ip = i + 1;
473 return (SNMP_CODE_FAILED);
474 }
475 }
476
477 return (SNMP_CODE_OK);
478}
479
480/*
481 * SNMP input. Start: decode the PDU, find the user or community.
482 */
483enum snmpd_input_err
484snmp_input_start(const u_char *buf, size_t len, const char *source,
485 struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen)
486{
487 struct asn_buf b;
488 enum snmp_code code;
489 enum snmpd_input_err ret;
490 int sret;
491
492 /* update uptime */
493 this_tick = get_ticks();
494
495 b.asn_cptr = buf;
496 b.asn_len = len;
497
498 /* look whether we have enough bytes for the entire PDU. */
499 switch (sret = snmp_pdu_snoop(&b)) {
500
501 case 0:
502 return (SNMPD_INPUT_TRUNC);
503
504 case -1:
505 snmpd_stats.inASNParseErrs++;
506 return (SNMPD_INPUT_FAILED);
507 }
508 b.asn_len = *pdulen = (size_t)sret;
509
510 memset(pdu, 0, sizeof(*pdu));
511 if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK)
512 goto decoded;
513
514 if (pdu->version == SNMP_V3) {
515 if (pdu->security_model != SNMP_SECMODEL_USM) {
516 code = SNMP_CODE_FAILED;
517 goto decoded;
518 }
519 if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK)
520 goto decoded;
521 if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK)
522 goto decoded;
523 }
524 code = snmp_pdu_decode_scoped(&b, pdu, ip);
525
526 ret = SNMPD_INPUT_OK;
527
528decoded:
529 snmpd_stats.inPkts++;
530
531 switch (code) {
532
533 case SNMP_CODE_FAILED:
534 snmpd_stats.inASNParseErrs++;
535 return (SNMPD_INPUT_FAILED);
536
537 case SNMP_CODE_BADVERS:
538 bad_vers:
539 snmpd_stats.inBadVersions++;
540 return (SNMPD_INPUT_FAILED);
541
542 case SNMP_CODE_BADLEN:
543 if (pdu->type == SNMP_OP_SET)
544 ret = SNMPD_INPUT_VALBADLEN;
545 break;
546
547 case SNMP_CODE_OORANGE:
548 if (pdu->type == SNMP_OP_SET)
549 ret = SNMPD_INPUT_VALRANGE;
550 break;
551
552 case SNMP_CODE_BADENC:
553 if (pdu->type == SNMP_OP_SET)
554 ret = SNMPD_INPUT_VALBADENC;
555 break;
556
557 case SNMP_CODE_BADSECLEVEL:
558 snmpd_usmstats.unsupported_seclevels++;
559 return (SNMPD_INPUT_FAILED);
560
561 case SNMP_CODE_NOTINTIME:
562 snmpd_usmstats.not_in_time_windows++;
563 return (SNMPD_INPUT_FAILED);
564
565 case SNMP_CODE_BADUSER:
566 snmpd_usmstats.unknown_users++;
567 return (SNMPD_INPUT_FAILED);
568
569 case SNMP_CODE_BADENGINE:
570 snmpd_usmstats.unknown_engine_ids++;
571 return (SNMPD_INPUT_FAILED);
572
573 case SNMP_CODE_BADDIGEST:
574 snmpd_usmstats.wrong_digests++;
575 return (SNMPD_INPUT_FAILED);
576
577 case SNMP_CODE_EDECRYPT:
578 snmpd_usmstats.decrypt_errors++;
579 return (SNMPD_INPUT_FAILED);
580
581 case SNMP_CODE_OK:
582 switch (pdu->version) {
583
584 case SNMP_V1:
585 if (!(snmpd.version_enable & VERS_ENABLE_V1))
586 goto bad_vers;
587 break;
588
589 case SNMP_V2c:
590 if (!(snmpd.version_enable & VERS_ENABLE_V2C))
591 goto bad_vers;
592 break;
593
594 case SNMP_V3:
595 if (!(snmpd.version_enable & VERS_ENABLE_V3))
596 goto bad_vers;
597 break;
598
599 case SNMP_Verr:
600 goto bad_vers;
601 }
602 break;
603 }
604
605 if (debug.dump_pdus) {
606 snmp_printf("%s -> ", source);
607 snmp_pdu_dump(pdu);
608 }
609
610 /*
611 * Look, whether we know the community or user
612 */
613
614 if (pdu->version != SNMP_V3) {
615 TAILQ_FOREACH(comm, &community_list, link)
616 if (comm->string != NULL &&
617 strcmp(comm->string, pdu->community) == 0)
618 break;
619
620 if (comm == NULL) {
621 snmpd_stats.inBadCommunityNames++;
622 snmp_pdu_free(pdu);
623 if (snmpd.auth_traps)
624 snmp_send_trap(&oid_authenticationFailure,
625 (struct snmp_value *)NULL);
626 ret = SNMPD_INPUT_BAD_COMM;
627 } else
628 community = comm->value;
629 } else if (pdu->nbindings == 0) {
630 /* RFC 3414 - snmpEngineID Discovery */
631 if (strlen(pdu->user.sec_name) == 0) {
632 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
633 &oid_usmUnknownEngineIDs);
634 pdu->context_engine_len = snmpd_engine.engine_len;
635 memcpy(pdu->context_engine, snmpd_engine.engine_id,
636 snmpd_engine.engine_len);
637 } else if (pdu->engine.engine_boots == 0 &&
638 pdu->engine.engine_time == 0) {
639 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
640 &oid_usmNotInTimeWindows);
641 pdu->engine.engine_boots = snmpd_engine.engine_boots;
642 pdu->engine.engine_time = snmpd_engine.engine_time;
643 }
644 } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH &&
645 (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) {
646 snmpd_usmstats.not_in_time_windows++;
647 ret = SNMP_CODE_FAILED;
648 }
649
650 if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK)
651 ret = SNMP_CODE_FAILED;
652
653 return (ret);
654}
655
656/*
657 * Will return only _OK or _FAILED
658 */
659enum snmpd_input_err
660snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen,
661 u_char *sndbuf, size_t *sndlen, const char *source,
662 enum snmpd_input_err ierr, int32_t ivar, void *data)
663{
664 struct snmp_pdu resp;
665 struct asn_buf resp_b, pdu_b;
666 enum snmp_ret ret;
667
668 resp_b.asn_ptr = sndbuf;
669 resp_b.asn_len = snmpd.txbuf;
670
671 pdu_b.asn_cptr = rcvbuf;
672 pdu_b.asn_len = rcvlen;
673
674 if (ierr != SNMPD_INPUT_OK) {
675 /* error decoding the input of a SET */
676 if (pdu->version == SNMP_V1)
677 pdu->error_status = SNMP_ERR_BADVALUE;
678 else if (ierr == SNMPD_INPUT_VALBADLEN)
679 pdu->error_status = SNMP_ERR_WRONG_LENGTH;
680 else if (ierr == SNMPD_INPUT_VALRANGE)
681 pdu->error_status = SNMP_ERR_WRONG_VALUE;
682 else
683 pdu->error_status = SNMP_ERR_WRONG_ENCODING;
684
685 pdu->error_index = ivar;
686
687 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
688 syslog(LOG_WARNING, "could not encode error response");
689 snmpd_stats.silentDrops++;
690 return (SNMPD_INPUT_FAILED);
691 }
692
693 if (debug.dump_pdus) {
694 snmp_printf("%s <- ", source);
695 snmp_pdu_dump(pdu);
696 }
697 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
698 return (SNMPD_INPUT_OK);
699 }
700
701 switch (pdu->type) {
702
703 case SNMP_PDU_GET:
704 ret = snmp_get(pdu, &resp_b, &resp, data);
705 break;
706
707 case SNMP_PDU_GETNEXT:
708 ret = snmp_getnext(pdu, &resp_b, &resp, data);
709 break;
710
711 case SNMP_PDU_SET:
712 ret = snmp_set(pdu, &resp_b, &resp, data);
713 break;
714
715 case SNMP_PDU_GETBULK:
716 ret = snmp_getbulk(pdu, &resp_b, &resp, data);
717 break;
718
719 default:
720 ret = SNMP_RET_IGN;
721 break;
722 }
723
724 switch (ret) {
725
726 case SNMP_RET_OK:
727 /* normal return - send a response */
728 if (debug.dump_pdus) {
729 snmp_printf("%s <- ", source);
730 snmp_pdu_dump(&resp);
731 }
732 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
733 snmp_pdu_free(&resp);
734 return (SNMPD_INPUT_OK);
735
736 case SNMP_RET_IGN:
737 /* error - send nothing */
738 snmpd_stats.silentDrops++;
739 return (SNMPD_INPUT_FAILED);
740
741 case SNMP_RET_ERR:
742 /* error - send error response. The snmp routine has
743 * changed the error fields in the original message. */
744 resp_b.asn_ptr = sndbuf;
745 resp_b.asn_len = snmpd.txbuf;
746 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
747 syslog(LOG_WARNING, "could not encode error response");
748 snmpd_stats.silentDrops++;
749 return (SNMPD_INPUT_FAILED);
750 } else {
751 if (debug.dump_pdus) {
752 snmp_printf("%s <- ", source);
753 snmp_pdu_dump(pdu);
754 }
755 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
756 return (SNMPD_INPUT_OK);
757 }
758 }
759 abort();
760}
761
762/*
763 * Insert a port into the right place in the transport's table of ports
764 */
765void
766trans_insert_port(struct transport *t, struct tport *port)
767{
768 struct tport *p;
769
770 TAILQ_FOREACH(p, &t->table, link) {
771 if (asn_compare_oid(&p->index, &port->index) > 0) {
772 TAILQ_INSERT_BEFORE(p, port, link);
773 return;
774 }
775 }
776 port->transport = t;
777 TAILQ_INSERT_TAIL(&t->table, port, link);
778}
779
780/*
781 * Remove a port from a transport's list
782 */
783void
784trans_remove_port(struct tport *port)
785{
786
787 TAILQ_REMOVE(&port->transport->table, port, link);
788}
789
790/*
791 * Find a port on a transport's list
792 */
793struct tport *
794trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub)
795{
796
797 return (FIND_OBJECT_OID(&t->table, idx, sub));
798}
799
800/*
801 * Find next port on a transport's list
802 */
803struct tport *
804trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub)
805{
806
807 return (NEXT_OBJECT_OID(&t->table, idx, sub));
808}
809
810/*
811 * Return first port
812 */
813struct tport *
814trans_first_port(struct transport *t)
815{
816
817 return (TAILQ_FIRST(&t->table));
818}
819
820/*
821 * Iterate through all ports until a function returns a 0.
822 */
823struct tport *
824trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t),
825 intptr_t arg)
826{
827 struct tport *p;
828
829 TAILQ_FOREACH(p, &t->table, link)
830 if (func(p, arg) == 0)
831 return (p);
832 return (NULL);
833}
834
835/*
836 * Register a transport
837 */
838int
839trans_register(const struct transport_def *def, struct transport **pp)
840{
841 u_int i;
842 char or_descr[256];
843
844 if ((*pp = malloc(sizeof(**pp))) == NULL)
845 return (SNMP_ERR_GENERR);
846
847 /* construct index */
848 (*pp)->index.len = strlen(def->name) + 1;
849 (*pp)->index.subs[0] = strlen(def->name);
850 for (i = 0; i < (*pp)->index.subs[0]; i++)
851 (*pp)->index.subs[i + 1] = def->name[i];
852
853 (*pp)->vtab = def;
854
855 if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) {
856 free(*pp);
857 return (SNMP_ERR_INCONS_VALUE);
858 }
859
860 /* register module */
861 snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name);
862 if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) {
863 free(*pp);
864 return (SNMP_ERR_GENERR);
865 }
866
867 INSERT_OBJECT_OID((*pp), &transport_list);
868
869 TAILQ_INIT(&(*pp)->table);
870
871 return (SNMP_ERR_NOERROR);
872}
873
874/*
875 * Unregister transport
876 */
877int
878trans_unregister(struct transport *t)
879{
880 if (!TAILQ_EMPTY(&t->table))
881 return (SNMP_ERR_INCONS_VALUE);
882
883 or_unregister(t->or_index);
884 TAILQ_REMOVE(&transport_list, t, link);
885
886 return (SNMP_ERR_NOERROR);
887}
888
889/*
890 * File descriptor support
891 */
892#ifdef USE_LIBBEGEMOT
893static void
894input(int fd, int mask __unused, void *uap)
895#else
896static void
897input(evContext ctx __unused, void *uap, int fd, int mask __unused)
898#endif
899{
900 struct fdesc *f = uap;
901
902 (*f->func)(fd, f->udata);
903}
904
905void
906fd_suspend(void *p)
907{
908 struct fdesc *f = p;
909
910#ifdef USE_LIBBEGEMOT
911 if (f->id >= 0) {
912 poll_unregister(f->id);
913 f->id = -1;
914 }
915#else
916 if (evTestID(f->id)) {
917 (void)evDeselectFD(evctx, f->id);
918 evInitID(&f->id);
919 }
920#endif
921}
922
923int
924fd_resume(void *p)
925{
926 struct fdesc *f = p;
927 int err;
928
929#ifdef USE_LIBBEGEMOT
930 if (f->id >= 0)
931 return (0);
932 if ((f->id = poll_register(f->fd, input, f, POLL_IN)) < 0) {
933 err = errno;
934 syslog(LOG_ERR, "select fd %d: %m", f->fd);
935 errno = err;
936 return (-1);
937 }
938#else
939 if (evTestID(f->id))
940 return (0);
941 if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) {
942 err = errno;
943 syslog(LOG_ERR, "select fd %d: %m", f->fd);
944 errno = err;
945 return (-1);
946 }
947#endif
948 return (0);
949}
950
951void *
952fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod)
953{
954 struct fdesc *f;
955 int err;
956
957 if ((f = malloc(sizeof(struct fdesc))) == NULL) {
958 err = errno;
959 syslog(LOG_ERR, "fd_select: %m");
960 errno = err;
961 return (NULL);
962 }
963 f->fd = fd;
964 f->func = func;
965 f->udata = udata;
966 f->owner = mod;
967#ifdef USE_LIBBEGEMOT
968 f->id = -1;
969#else
970 evInitID(&f->id);
971#endif
972
973 if (fd_resume(f)) {
974 err = errno;
975 free(f);
976 errno = err;
977 return (NULL);
978 }
979
980 LIST_INSERT_HEAD(&fdesc_list, f, link);
981
982 return (f);
983}
984
985void
986fd_deselect(void *p)
987{
988 struct fdesc *f = p;
989
990 LIST_REMOVE(f, link);
991 fd_suspend(f);
992 free(f);
993}
994
995static void
996fd_flush(struct lmodule *mod)
997{
998 struct fdesc *t, *t1;
999
1000 t = LIST_FIRST(&fdesc_list);
1001 while (t != NULL) {
1002 t1 = LIST_NEXT(t, link);
1003 if (t->owner == mod)
1004 fd_deselect(t);
1005 t = t1;
1006 }
1007}
1008
1009/*
1010 * Consume a message from the input buffer
1011 */
1012static void
1013snmp_input_consume(struct port_input *pi)
1014{
1015 if (!pi->stream) {
1016 /* always consume everything */
1017 pi->length = 0;
1018 return;
1019 }
1020 if (pi->consumed >= pi->length) {
1021 /* all bytes consumed */
1022 pi->length = 0;
1023 return;
1024 }
1025 memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed);
1026 pi->length -= pi->consumed;
1027}
1028
1029struct credmsg {
1030 struct cmsghdr hdr;
1031 struct cmsgcred cred;
1032};
1033
1034static void
1035check_priv(struct port_input *pi, struct msghdr *msg)
1036{
1037 struct credmsg *cmsg;
1038 struct xucred ucred;
1039 socklen_t ucredlen;
1040
1041 pi->priv = 0;
1042
1043 if (msg->msg_controllen == sizeof(*cmsg)) {
1044 /* process explicitly sends credentials */
1045
1046 cmsg = (struct credmsg *)msg->msg_control;
1047 pi->priv = (cmsg->cred.cmcred_euid == 0);
1048 return;
1049 }
1050
1051 /* ok, obtain the accept time credentials */
1052 ucredlen = sizeof(ucred);
1053
1054 if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
1055 ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
1056 pi->priv = (ucred.cr_uid == 0);
1057}
1058
1059/*
1060 * Input from a stream socket.
1061 */
1062static int
1063recv_stream(struct port_input *pi)
1064{
1065 struct msghdr msg;
1066 struct iovec iov[1];
1067 ssize_t len;
1068 struct credmsg cmsg;
1069
1070 if (pi->buf == NULL) {
1071 /* no buffer yet - allocate one */
1072 if ((pi->buf = buf_alloc(0)) == NULL) {
1073 /* ups - could not get buffer. Return an error
1074 * the caller must close the transport. */
1075 return (-1);
1076 }
1077 pi->buflen = buf_size(0);
1078 pi->consumed = 0;
1079 pi->length = 0;
1080 }
1081
1082 /* try to get a message */
1083 msg.msg_name = pi->peer;
1084 msg.msg_namelen = pi->peerlen;
1085 msg.msg_iov = iov;
1086 msg.msg_iovlen = 1;
1087 if (pi->cred) {
1088 msg.msg_control = &cmsg;
1089 msg.msg_controllen = sizeof(cmsg);
1090
1091 cmsg.hdr.cmsg_len = sizeof(cmsg);
1092 cmsg.hdr.cmsg_level = SOL_SOCKET;
1093 cmsg.hdr.cmsg_type = SCM_CREDS;
1094 } else {
1095 msg.msg_control = NULL;
1096 msg.msg_controllen = 0;
1097 }
1098 msg.msg_flags = 0;
1099
1100 iov[0].iov_base = pi->buf + pi->length;
1101 iov[0].iov_len = pi->buflen - pi->length;
1102
1103 len = recvmsg(pi->fd, &msg, 0);
1104
1105 if (len == -1 || len == 0)
1106 /* receive error */
1107 return (-1);
1108
1109 pi->length += len;
1110
1111 if (pi->cred)
1112 check_priv(pi, &msg);
1113
1114 return (0);
1115}
1116
1117/*
1118 * Input from a datagram socket.
1119 * Each receive should return one datagram.
1120 */
1121static int
1122recv_dgram(struct port_input *pi)
1123{
1124 u_char embuf[1000];
1125 struct msghdr msg;
1126 struct iovec iov[1];
1127 ssize_t len;
1128 struct credmsg cmsg;
1129
1130 if (pi->buf == NULL) {
1131 /* no buffer yet - allocate one */
1132 if ((pi->buf = buf_alloc(0)) == NULL) {
1133 /* ups - could not get buffer. Read away input
1134 * and drop it */
1135 (void)recvfrom(pi->fd, embuf, sizeof(embuf),
1136 0, NULL, NULL);
1137 /* return error */
1138 return (-1);
1139 }
1140 pi->buflen = buf_size(0);
1141 }
1142
1143 /* try to get a message */
1144 msg.msg_name = pi->peer;
1145 msg.msg_namelen = pi->peerlen;
1146 msg.msg_iov = iov;
1147 msg.msg_iovlen = 1;
1148 if (pi->cred) {
1149 msg.msg_control = &cmsg;
1150 msg.msg_controllen = sizeof(cmsg);
1151
1152 cmsg.hdr.cmsg_len = sizeof(cmsg);
1153 cmsg.hdr.cmsg_level = SOL_SOCKET;
1154 cmsg.hdr.cmsg_type = SCM_CREDS;
1155 } else {
1156 msg.msg_control = NULL;
1157 msg.msg_controllen = 0;
1158 }
1159 msg.msg_flags = 0;
1160
1161 iov[0].iov_base = pi->buf;
1162 iov[0].iov_len = pi->buflen;
1163
1164 len = recvmsg(pi->fd, &msg, 0);
1165
1166 if (len == -1 || len == 0)
1167 /* receive error */
1168 return (-1);
1169
1170 if (msg.msg_flags & MSG_TRUNC) {
1171 /* truncated - drop */
1172 snmpd_stats.silentDrops++;
1173 snmpd_stats.inTooLong++;
1174 return (-1);
1175 }
1176
1177 pi->length = (size_t)len;
1178
1179 if (pi->cred)
1180 check_priv(pi, &msg);
1181
1182 return (0);
1183}
1184
1185/*
1186 * Input from a socket
1187 */
1188int
1189snmpd_input(struct port_input *pi, struct tport *tport)
1190{
1191 u_char *sndbuf;
1192 size_t sndlen;
1193 struct snmp_pdu pdu;
1194 enum snmpd_input_err ierr, ferr;
1195 enum snmpd_proxy_err perr;
1196 int32_t vi;
1197 int ret;
1198 ssize_t slen;
1199#ifdef USE_TCPWRAPPERS
1200 char client[16];
1201#endif
1202
1203 /* get input depending on the transport */
1204 if (pi->stream) {
1205 ret = recv_stream(pi);
1206 } else {
1207 ret = recv_dgram(pi);
1208 }
1209
1210 if (ret == -1)
1211 return (-1);
1212
1213#ifdef USE_TCPWRAPPERS
1214 /*
1215 * In case of AF_INET{6} peer, do hosts_access(5) check.
1216 */
1217 if (inet_ntop(pi->peer->sa_family,
1218 &((const struct sockaddr_in *)(const void *)pi->peer)->sin_addr,
1219 client, sizeof(client)) != NULL) {
1220 request_set(&req, RQ_CLIENT_ADDR, client, 0);
1221 if (hosts_access(&req) == 0) {
1222 syslog(LOG_ERR, "refused connection from %.500s",
1223 eval_client(&req));
1224 return (-1);
1225 }
1226 } else
1227 syslog(LOG_ERR, "inet_ntop(): %m");
1228#endif
1229
1230 /*
1231 * Handle input
1232 */
1233 ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi,
1234 &pi->consumed);
1235 if (ierr == SNMPD_INPUT_TRUNC) {
1236 /* need more bytes. This is ok only for streaming transports.
1237 * but only if we have not reached bufsiz yet. */
1238 if (pi->stream) {
1239 if (pi->length == buf_size(0)) {
1240 snmpd_stats.silentDrops++;
1241 return (-1);
1242 }
1243 return (0);
1244 }
1245 snmpd_stats.silentDrops++;
1246 return (-1);
1247 }
1248
1249 /* can't check for bad SET pdus here, because a proxy may have to
1250 * check the access first. We don't want to return an error response
1251 * to a proxy PDU with a wrong community */
1252 if (ierr == SNMPD_INPUT_FAILED) {
1253 /* for streaming transports this is fatal */
1254 if (pi->stream)
1255 return (-1);
1256 snmp_input_consume(pi);
1257 return (0);
1258 }
1259 if (ierr == SNMPD_INPUT_BAD_COMM) {
1260 snmp_input_consume(pi);
1261 return (0);
1262 }
1263
1264 /*
1265 * If that is a module community and the module has a proxy function,
1266 * the hand it over to the module.
1267 */
1268 if (comm != NULL && comm->owner != NULL &&
1269 comm->owner->config->proxy != NULL) {
1270 perr = (*comm->owner->config->proxy)(&pdu, tport->transport,
1271 &tport->index, pi->peer, pi->peerlen, ierr, vi,
1272 !pi->cred || pi->priv);
1273
1274 switch (perr) {
1275
1276 case SNMPD_PROXY_OK:
1277 snmp_input_consume(pi);
1278 return (0);
1279
1280 case SNMPD_PROXY_REJ:
1281 break;
1282
1283 case SNMPD_PROXY_DROP:
1284 snmp_input_consume(pi);
1285 snmp_pdu_free(&pdu);
1286 snmpd_stats.proxyDrops++;
1287 return (0);
1288
1289 case SNMPD_PROXY_BADCOMM:
1290 snmp_input_consume(pi);
1291 snmp_pdu_free(&pdu);
1292 snmpd_stats.inBadCommunityNames++;
1293 if (snmpd.auth_traps)
1294 snmp_send_trap(&oid_authenticationFailure,
1295 (struct snmp_value *)NULL);
1296 return (0);
1297
1298 case SNMPD_PROXY_BADCOMMUSE:
1299 snmp_input_consume(pi);
1300 snmp_pdu_free(&pdu);
1301 snmpd_stats.inBadCommunityUses++;
1302 if (snmpd.auth_traps)
1303 snmp_send_trap(&oid_authenticationFailure,
1304 (struct snmp_value *)NULL);
1305 return (0);
1306 }
1307 }
1308
1309 /*
1310 * Check type
1311 */
1312 if (pdu.type == SNMP_PDU_RESPONSE ||
1313 pdu.type == SNMP_PDU_TRAP ||
1314 pdu.type == SNMP_PDU_TRAP2) {
1315 snmpd_stats.silentDrops++;
1316 snmpd_stats.inBadPduTypes++;
1317 snmp_pdu_free(&pdu);
1318 snmp_input_consume(pi);
1319 return (0);
1320 }
1321
1322 /*
1323 * Check community
1324 */
1325 if (pdu.version < SNMP_V3 &&
1326 ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) ||
1327 (community != COMM_WRITE &&
1328 (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) {
1329 snmpd_stats.inBadCommunityUses++;
1330 snmp_pdu_free(&pdu);
1331 snmp_input_consume(pi);
1332 if (snmpd.auth_traps)
1333 snmp_send_trap(&oid_authenticationFailure,
1334 (struct snmp_value *)NULL);
1335 return (0);
1336 }
1337
1338 /*
1339 * Execute it.
1340 */
1341 if ((sndbuf = buf_alloc(1)) == NULL) {
1342 snmpd_stats.silentDrops++;
1343 snmp_pdu_free(&pdu);
1344 snmp_input_consume(pi);
1345 return (0);
1346 }
1347 ferr = snmp_input_finish(&pdu, pi->buf, pi->length,
1348 sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
1349
1350 if (ferr == SNMPD_INPUT_OK) {
1351 slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen);
1352 if (slen == -1)
1353 syslog(LOG_ERR, "sendto: %m");
1354 else if ((size_t)slen != sndlen)
1355 syslog(LOG_ERR, "sendto: short write %zu/%zu",
1356 sndlen, (size_t)slen);
1357 }
1358 snmp_pdu_free(&pdu);
1359 free(sndbuf);
1360 snmp_input_consume(pi);
1361
1362 return (0);
1363}
1364
1365/*
1366 * Send a PDU to a given port
1367 */
1368void
1369snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu,
1370 const struct sockaddr *addr, socklen_t addrlen)
1371{
1372 struct transport *trans = targ;
1373 struct tport *tp;
1374 u_char *sndbuf;
1375 size_t sndlen;
1376 ssize_t len;
1377
1378 TAILQ_FOREACH(tp, &trans->table, link)
1379 if (asn_compare_oid(port, &tp->index) == 0)
1380 break;
1381 if (tp == 0)
1382 return;
1383
1384 if ((sndbuf = buf_alloc(1)) == NULL)
1385 return;
1386
1387 snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY");
1388
1389 len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen);
1390
1391 if (len == -1)
1392 syslog(LOG_ERR, "sendto: %m");
1393 else if ((size_t)len != sndlen)
1394 syslog(LOG_ERR, "sendto: short write %zu/%zu",
1395 sndlen, (size_t)len);
1396
1397 free(sndbuf);
1398}
1399
1400
1401/*
1402 * Close an input source
1403 */
1404void
1405snmpd_input_close(struct port_input *pi)
1406{
1407 if (pi->id != NULL)
1408 fd_deselect(pi->id);
1409 if (pi->fd >= 0)
1410 (void)close(pi->fd);
1411 if (pi->buf != NULL)
1412 free(pi->buf);
1413}
1414
1415/*
1416 * Dump internal state.
1417 */
1418#ifdef USE_LIBBEGEMOT
1419static void
1420info_func(void)
1421#else
1422static void
1423info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused)
1424#endif
1425{
1426 struct lmodule *m;
1427 u_int i;
1428 char buf[10000];
1429
1430 syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid());
1431 for (i = 0; i < tree_size; i++) {
1432 switch (tree[i].type) {
1433
1434 case SNMP_NODE_LEAF:
1435 sprintf(buf, "LEAF: %s %s", tree[i].name,
1436 asn_oid2str(&tree[i].oid));
1437 break;
1438
1439 case SNMP_NODE_COLUMN:
1440 sprintf(buf, "COL: %s %s", tree[i].name,
1441 asn_oid2str(&tree[i].oid));
1442 break;
1443 }
1444 syslog(LOG_DEBUG, "%s", buf);
1445 }
1446
1447 TAILQ_FOREACH(m, &lmodules, link)
1448 if (m->config->dump)
1449 (*m->config->dump)();
1450}
1451
1452/*
1453 * Re-read configuration
1454 */
1455#ifdef USE_LIBBEGEMOT
1456static void
1457config_func(void)
1458#else
1459static void
1460config_func(evContext ctx __unused, void *uap __unused,
1461 const void *tag __unused)
1462#endif
1463{
1464 struct lmodule *m;
1465
1466 if (read_config(config_file, NULL)) {
1467 syslog(LOG_ERR, "error reading config file '%s'", config_file);
1468 return;
1469 }
1470 TAILQ_FOREACH(m, &lmodules, link)
1471 if (m->config->config)
1472 (*m->config->config)();
1473}
1474
1475/*
1476 * On USR1 dump actual configuration.
1477 */
1478static void
1479onusr1(int s __unused)
1480{
1481
1482 work |= WORK_DOINFO;
1483}
1484static void
1485onhup(int s __unused)
1486{
1487
1488 work |= WORK_RECONFIG;
1489}
1490
1491static void
1492onterm(int s __unused)
1493{
1494
1495 /* allow clean-up */
1496 exit(0);
1497}
1498
1499static void
1500init_sigs(void)
1501{
1502 struct sigaction sa;
1503
1504 sa.sa_handler = onusr1;
1505 sa.sa_flags = SA_RESTART;
1506 sigemptyset(&sa.sa_mask);
1507 if (sigaction(SIGUSR1, &sa, NULL)) {
1508 syslog(LOG_ERR, "sigaction: %m");
1509 exit(1);
1510 }
1511
1512 sa.sa_handler = onhup;
1513 if (sigaction(SIGHUP, &sa, NULL)) {
1514 syslog(LOG_ERR, "sigaction: %m");
1515 exit(1);
1516 }
1517
1518 sa.sa_handler = onterm;
1519 sa.sa_flags = 0;
1520 sigemptyset(&sa.sa_mask);
1521 if (sigaction(SIGTERM, &sa, NULL)) {
1522 syslog(LOG_ERR, "sigaction: %m");
1523 exit(1);
1524 }
1525 if (sigaction(SIGINT, &sa, NULL)) {
1526 syslog(LOG_ERR, "sigaction: %m");
1527 exit(1);
1528 }
1529}
1530
1531static void
1532block_sigs(void)
1533{
1534 sigset_t set;
1535
1536 sigfillset(&set);
1537 if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) {
1538 syslog(LOG_ERR, "SIG_BLOCK: %m");
1539 exit(1);
1540 }
1541}
1542static void
1543unblock_sigs(void)
1544{
1545 if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) {
1546 syslog(LOG_ERR, "SIG_SETMASK: %m");
1547 exit(1);
1548 }
1549}
1550
1551/*
1552 * Shut down
1553 */
1554static void
1555term(void)
1556{
1557 (void)unlink(pid_file);
1558}
1559
1560static void
1561trans_stop(void)
1562{
1563 struct transport *t;
1564
1565 TAILQ_FOREACH(t, &transport_list, link)
1566 (void)t->vtab->stop(1);
1567}
1568
1569/*
1570 * Define a macro from the command line
1571 */
1572static void
1573do_macro(char *arg)
1574{
1575 char *eq;
1576 int err;
1577
1578 if ((eq = strchr(arg, '=')) == NULL)
1579 err = define_macro(arg, "");
1580 else {
1581 *eq++ = '\0';
1582 err = define_macro(arg, eq);
1583 }
1584 if (err == -1) {
1585 syslog(LOG_ERR, "cannot save macro: %m");
1586 exit(1);
1587 }
1588}
1589
1590/*
1591 * Re-implement getsubopt from scratch, because the second argument is broken
1592 * and will not compile with WARNS=5.
1593 */
1594static int
1595getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
1596{
1597 static const char *const delim = ",\t ";
1598 u_int i;
1599 char *ptr;
1600
1601 *optp = NULL;
1602
1603 /* skip leading junk */
1604 for (ptr = *arg; *ptr != '\0'; ptr++)
1605 if (strchr(delim, *ptr) == NULL)
1606 break;
1607 if (*ptr == '\0') {
1608 *arg = ptr;
1609 return (-1);
1610 }
1611 *optp = ptr;
1612
1613 /* find the end of the option */
1614 while (*++ptr != '\0')
1615 if (strchr(delim, *ptr) != NULL || *ptr == '=')
1616 break;
1617
1618 if (*ptr != '\0') {
1619 if (*ptr == '=') {
1620 *ptr++ = '\0';
1621 *valp = ptr;
1622 while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
1623 ptr++;
1624 if (*ptr != '\0')
1625 *ptr++ = '\0';
1626 } else
1627 *ptr++ = '\0';
1628 }
1629
1630 *arg = ptr;
1631
1632 for (i = 0; *options != NULL; options++, i++)
1633 if (strcmp(*optp, *options) == 0)
1634 return (i);
1635 return (-1);
1636}
1637
1638int
1639main(int argc, char *argv[])
1640{
1641 int opt;
1642 FILE *fp;
1643 int background = 1;
1644 struct tport *p;
1645 const char *prefix = "snmpd";
1646 struct lmodule *m;
1647 char *value = NULL, *option; /* XXX */
1648 struct transport *t;
1649
1650#define DBG_DUMP 0
1651#define DBG_EVENTS 1
1652#define DBG_TRACE 2
1653 static const char *const debug_opts[] = {
1654 "dump",
1655 "events",
1656 "trace",
1657 NULL
1658 };
1659
1660 snmp_printf = snmp_printf_func;
1661 snmp_error = snmp_error_func;
1662 snmp_debug = snmp_debug_func;
1663 asn_error = asn_error_func;
1664
1665 while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF)
1666 switch (opt) {
1667
1668 case 'c':
1669 strlcpy(config_file, optarg, sizeof(config_file));
1670 break;
1671
1672 case 'd':
1673 background = 0;
1674 break;
1675
1676 case 'D':
1677 while (*optarg) {
1678 switch (getsubopt1(&optarg, debug_opts,
1679 &value, &option)) {
1680
1681 case DBG_DUMP:
1682 debug.dump_pdus = 1;
1683 break;
1684
1685 case DBG_EVENTS:
1686 debug.evdebug++;
1687 break;
1688
1689 case DBG_TRACE:
1690 if (value == NULL)
1691 syslog(LOG_ERR,
1692 "no value for 'trace'");
1693 else
1694 snmp_trace = strtoul(value,
1695 NULL, 0);
1696 break;
1697
1698 case -1:
1699 if (suboptarg)
1700 syslog(LOG_ERR,
1701 "unknown debug flag '%s'",
1702 option);
1703 else
1704 syslog(LOG_ERR,
1705 "missing debug flag");
1706 break;
1707 }
1708 }
1709 break;
1710
1711 case 'e':
1712 strlcpy(engine_file, optarg, sizeof(engine_file));
1713 break;
1714 case 'h':
1715 fprintf(stderr, "%s", usgtxt);
1716 exit(0);
1717
1718 case 'I':
1719 syspath = optarg;
1720 break;
1721
1722 case 'l':
1723 prefix = optarg;
1724 break;
1725
1726 case 'm':
1727 do_macro(optarg);
1728 break;
1729
1730 case 'p':
1731 strlcpy(pid_file, optarg, sizeof(pid_file));
1732 break;
1733 }
1734
1735 openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER);
1736 setlogmask(LOG_UPTO(debug.logpri - 1));
1737
1738 if (background && daemon(0, 0) < 0) {
1739 syslog(LOG_ERR, "daemon: %m");
1740 exit(1);
1741 }
1742
1743 argc -= optind;
1744 argv += optind;
1745
1746 progargs = argv;
1747 nprogargs = argc;
1748
1749 srandomdev();
1750
1751 snmp_serial_no = random();
1752
1753#ifdef USE_TCPWRAPPERS
1754 /*
1755 * Initialize hosts_access(3) handler.
1756 */
1757 request_init(&req, RQ_DAEMON, "snmpd", 0);
1758 sock_methods(&req);
1759#endif
1760
1761 /*
1762 * Initialize the tree.
1763 */
1764 if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) {
1765 syslog(LOG_ERR, "%m");
1766 exit(1);
1767 }
1768 memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE);
1769 tree_size = CTREE_SIZE;
1770
1771 /*
1772 * Get standard communities
1773 */
1774 (void)comm_define(1, "SNMP read", NULL, NULL);
1775 (void)comm_define(2, "SNMP write", NULL, NULL);
1776 community = COMM_INITIALIZE;
1777
1778 trap_reqid = reqid_allocate(512, NULL);
1779
1780 if (config_file[0] == '\0')
1781 snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix);
1782
1783 init_actvals();
1784 init_snmpd_engine();
1785
1786 this_tick = get_ticks();
1787 start_tick = this_tick;
1788
1789 /* start transports */
1790 if (atexit(trans_stop) == -1) {
1791 syslog(LOG_ERR, "atexit failed: %m");
1792 exit(1);
1793 }
1794 if (udp_trans.start() != SNMP_ERR_NOERROR)
1795 syslog(LOG_WARNING, "cannot start UDP transport");
1796 if (lsock_trans.start() != SNMP_ERR_NOERROR)
1797 syslog(LOG_WARNING, "cannot start LSOCK transport");
1798
1799#ifdef USE_LIBBEGEMOT
1800 if (debug.evdebug > 0)
1801 rpoll_trace = 1;
1802#else
1803 if (evCreate(&evctx)) {
1804 syslog(LOG_ERR, "evCreate: %m");
1805 exit(1);
1806 }
1807 if (debug.evdebug > 0)
1808 evSetDebug(evctx, 10, stderr);
1809#endif
1810
1811 if (engine_file[0] == '\0')
1812 snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix);
1813
1814 if (read_config(config_file, NULL)) {
1815 syslog(LOG_ERR, "error in config file");
1816 exit(1);
1817 }
1818
1819 TAILQ_FOREACH(t, &transport_list, link)
1820 TAILQ_FOREACH(p, &t->table, link)
1821 t->vtab->init_port(p);
1822
1823 init_sigs();
1824
1825 if (pid_file[0] == '\0')
1826 snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix);
1827
1828 if ((fp = fopen(pid_file, "w")) != NULL) {
1829 fprintf(fp, "%u", getpid());
1830 fclose(fp);
1831 if (atexit(term) == -1) {
1832 syslog(LOG_ERR, "atexit failed: %m");
1833 (void)remove(pid_file);
1834 exit(0);
1835 }
1836 }
1837
1838 if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.",
1839 NULL) == 0) {
1840 syslog(LOG_ERR, "cannot register SNMPv2 MIB");
1841 exit(1);
1842 }
1843 if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.",
1844 NULL) == 0) {
1845 syslog(LOG_ERR, "cannot register begemotSnmpd MIB");
1846 exit(1);
1847 }
1848
1841 snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL);
1842
1843 while ((m = TAILQ_FIRST(&modules_start)) != NULL) {
1844 m->flags &= ~LM_ONSTARTLIST;
1845 TAILQ_REMOVE(&modules_start, m, start);
1846 lm_start(m);
1847 }
1848
1849 while ((m = TAILQ_FIRST(&modules_start)) != NULL) {
1850 m->flags &= ~LM_ONSTARTLIST;
1851 TAILQ_REMOVE(&modules_start, m, start);
1852 lm_start(m);
1853 }
1854
1855 snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL);
1856
1849 for (;;) {
1850#ifndef USE_LIBBEGEMOT
1851 evEvent event;
1852#endif
1853 struct lmodule *mod;
1854
1855 TAILQ_FOREACH(mod, &lmodules, link)
1856 if (mod->config->idle != NULL)
1857 (*mod->config->idle)();
1858
1859#ifndef USE_LIBBEGEMOT
1860 if (evGetNext(evctx, &event, EV_WAIT) == 0) {
1861 if (evDispatch(evctx, event))
1862 syslog(LOG_ERR, "evDispatch: %m");
1863 } else if (errno != EINTR) {
1864 syslog(LOG_ERR, "evGetNext: %m");
1865 exit(1);
1866 }
1867#else
1868 poll_dispatch(1);
1869#endif
1870
1871 if (work != 0) {
1872 block_sigs();
1873 if (work & WORK_DOINFO) {
1874#ifdef USE_LIBBEGEMOT
1875 info_func();
1876#else
1877 if (evWaitFor(evctx, &work, info_func,
1878 NULL, NULL) == -1) {
1879 syslog(LOG_ERR, "evWaitFor: %m");
1880 exit(1);
1881 }
1882#endif
1883 }
1884 if (work & WORK_RECONFIG) {
1885#ifdef USE_LIBBEGEMOT
1886 config_func();
1887#else
1888 if (evWaitFor(evctx, &work, config_func,
1889 NULL, NULL) == -1) {
1890 syslog(LOG_ERR, "evWaitFor: %m");
1891 exit(1);
1892 }
1893#endif
1894 }
1895 work = 0;
1896 unblock_sigs();
1897#ifndef USE_LIBBEGEMOT
1898 if (evDo(evctx, &work) == -1) {
1899 syslog(LOG_ERR, "evDo: %m");
1900 exit(1);
1901 }
1902#endif
1903 }
1904 }
1905
1906 return (0);
1907}
1908
1909uint64_t
1910get_ticks(void)
1911{
1912 struct timeval tv;
1913 uint64_t ret;
1914
1915 if (gettimeofday(&tv, NULL))
1916 abort();
1917 ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL;
1918 return (ret);
1919}
1920
1921/*
1922 * Timer support
1923 */
1924
1925/*
1926 * Trampoline for the non-repeatable timers.
1927 */
1928#ifdef USE_LIBBEGEMOT
1929static void
1930tfunc(int tid __unused, void *uap)
1931#else
1932static void
1933tfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
1934 struct timespec inter __unused)
1935#endif
1936{
1937 struct timer *tp = uap;
1938
1939 LIST_REMOVE(tp, link);
1940 tp->func(tp->udata);
1941 free(tp);
1942}
1943
1944/*
1945 * Trampoline for the repeatable timers.
1946 */
1947#ifdef USE_LIBBEGEMOT
1948static void
1949trfunc(int tid __unused, void *uap)
1950#else
1951static void
1952trfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
1953 struct timespec inter __unused)
1954#endif
1955{
1956 struct timer *tp = uap;
1957
1958 tp->func(tp->udata);
1959}
1960
1961/*
1962 * Start a one-shot timer
1963 */
1964void *
1965timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod)
1966{
1967 struct timer *tp;
1968#ifndef USE_LIBBEGEMOT
1969 struct timespec due;
1970#endif
1971
1972 if ((tp = malloc(sizeof(struct timer))) == NULL) {
1973 syslog(LOG_CRIT, "out of memory for timer");
1974 exit(1);
1975 }
1976
1977#ifndef USE_LIBBEGEMOT
1978 due = evAddTime(evNowTime(),
1979 evConsTime(ticks / 100, (ticks % 100) * 10000));
1980#endif
1981
1982 tp->udata = udata;
1983 tp->owner = mod;
1984 tp->func = func;
1985
1986 LIST_INSERT_HEAD(&timer_list, tp, link);
1987
1988#ifdef USE_LIBBEGEMOT
1989 if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) {
1990 syslog(LOG_ERR, "cannot set timer: %m");
1991 exit(1);
1992 }
1993#else
1994 if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id)
1995 == -1) {
1996 syslog(LOG_ERR, "cannot set timer: %m");
1997 exit(1);
1998 }
1999#endif
2000 return (tp);
2001}
2002
2003/*
2004 * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument
2005 * is currently ignored and the initial number of ticks is set to the
2006 * repeat number of ticks.
2007 */
2008void *
2009timer_start_repeat(u_int ticks __unused, u_int repeat_ticks,
2010 void (*func)(void *), void *udata, struct lmodule *mod)
2011{
2012 struct timer *tp;
2013#ifndef USE_LIBBEGEMOT
2014 struct timespec due;
2015 struct timespec inter;
2016#endif
2017
2018 if ((tp = malloc(sizeof(struct timer))) == NULL) {
2019 syslog(LOG_CRIT, "out of memory for timer");
2020 exit(1);
2021 }
2022
2023#ifndef USE_LIBBEGEMOT
2024 due = evAddTime(evNowTime(),
2025 evConsTime(ticks / 100, (ticks % 100) * 10000));
2026 inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000);
2027#endif
2028
2029 tp->udata = udata;
2030 tp->owner = mod;
2031 tp->func = func;
2032
2033 LIST_INSERT_HEAD(&timer_list, tp, link);
2034
2035#ifdef USE_LIBBEGEMOT
2036 if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) {
2037 syslog(LOG_ERR, "cannot set timer: %m");
2038 exit(1);
2039 }
2040#else
2041 if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) {
2042 syslog(LOG_ERR, "cannot set timer: %m");
2043 exit(1);
2044 }
2045#endif
2046 return (tp);
2047}
2048
2049/*
2050 * Stop a timer.
2051 */
2052void
2053timer_stop(void *p)
2054{
2055 struct timer *tp = p;
2056
2057 LIST_REMOVE(tp, link);
2058#ifdef USE_LIBBEGEMOT
2059 poll_stop_timer(tp->id);
2060#else
2061 if (evClearTimer(evctx, tp->id) == -1) {
2062 syslog(LOG_ERR, "cannot stop timer: %m");
2063 exit(1);
2064 }
2065#endif
2066 free(p);
2067}
2068
2069static void
2070timer_flush(struct lmodule *mod)
2071{
2072 struct timer *t, *t1;
2073
2074 t = LIST_FIRST(&timer_list);
2075 while (t != NULL) {
2076 t1 = LIST_NEXT(t, link);
2077 if (t->owner == mod)
2078 timer_stop(t);
2079 t = t1;
2080 }
2081}
2082
2083static void
2084snmp_printf_func(const char *fmt, ...)
2085{
2086 va_list ap;
2087 static char *pend = NULL;
2088 char *ret, *new;
2089
2090 va_start(ap, fmt);
2091 vasprintf(&ret, fmt, ap);
2092 va_end(ap);
2093
2094 if (ret == NULL)
2095 return;
2096 if (pend != NULL) {
2097 if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1))
2098 == NULL) {
2099 free(ret);
2100 return;
2101 }
2102 pend = new;
2103 strcat(pend, ret);
2104 free(ret);
2105 } else
2106 pend = ret;
2107
2108 while ((ret = strchr(pend, '\n')) != NULL) {
2109 *ret = '\0';
2110 syslog(LOG_DEBUG, "%s", pend);
2111 if (strlen(ret + 1) == 0) {
2112 free(pend);
2113 pend = NULL;
2114 break;
2115 }
2116 strcpy(pend, ret + 1);
2117 }
2118}
2119
2120static void
2121snmp_error_func(const char *err, ...)
2122{
2123 char errbuf[1000];
2124 va_list ap;
2125
2126 if (!(snmp_trace & LOG_SNMP_ERRORS))
2127 return;
2128
2129 va_start(ap, err);
2130 snprintf(errbuf, sizeof(errbuf), "SNMP: ");
2131 vsnprintf(errbuf + strlen(errbuf),
2132 sizeof(errbuf) - strlen(errbuf), err, ap);
2133 va_end(ap);
2134
2135 syslog(LOG_ERR, "%s", errbuf);
2136}
2137
2138static void
2139snmp_debug_func(const char *err, ...)
2140{
2141 char errbuf[1000];
2142 va_list ap;
2143
2144 va_start(ap, err);
2145 snprintf(errbuf, sizeof(errbuf), "SNMP: ");
2146 vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
2147 err, ap);
2148 va_end(ap);
2149
2150 syslog(LOG_DEBUG, "%s", errbuf);
2151}
2152
2153static void
2154asn_error_func(const struct asn_buf *b, const char *err, ...)
2155{
2156 char errbuf[1000];
2157 va_list ap;
2158 u_int i;
2159
2160 if (!(snmp_trace & LOG_ASN1_ERRORS))
2161 return;
2162
2163 va_start(ap, err);
2164 snprintf(errbuf, sizeof(errbuf), "ASN.1: ");
2165 vsnprintf(errbuf + strlen(errbuf),
2166 sizeof(errbuf) - strlen(errbuf), err, ap);
2167 va_end(ap);
2168
2169 if (b != NULL) {
2170 snprintf(errbuf + strlen(errbuf),
2171 sizeof(errbuf) - strlen(errbuf), " at");
2172 for (i = 0; b->asn_len > i; i++)
2173 snprintf(errbuf + strlen(errbuf),
2174 sizeof(errbuf) - strlen(errbuf),
2175 " %02x", b->asn_cptr[i]);
2176 }
2177
2178 syslog(LOG_ERR, "%s", errbuf);
2179}
2180
2181/*
2182 * Create a new community
2183 */
2184u_int
2185comm_define(u_int priv, const char *descr, struct lmodule *owner,
2186 const char *str)
2187{
2188 struct community *c, *p;
2189 u_int ncomm;
2190
2191 /* generate an identifier */
2192 do {
2193 if ((ncomm = next_community_index++) == UINT_MAX)
2194 next_community_index = 1;
2195 TAILQ_FOREACH(c, &community_list, link)
2196 if (c->value == ncomm)
2197 break;
2198 } while (c != NULL);
2199
2200 if ((c = malloc(sizeof(struct community))) == NULL) {
2201 syslog(LOG_ERR, "comm_define: %m");
2202 return (0);
2203 }
2204 c->owner = owner;
2205 c->value = ncomm;
2206 c->descr = descr;
2207 c->string = NULL;
2208 c->private = priv;
2209
2210 if (str != NULL) {
2211 if((c->string = malloc(strlen(str)+1)) == NULL) {
2212 free(c);
2213 return (0);
2214 }
2215 strcpy(c->string, str);
2216 }
2217
2218 /* make index */
2219 if (c->owner == NULL) {
2220 c->index.len = 1;
2221 c->index.subs[0] = 0;
2222 } else {
2223 c->index = c->owner->index;
2224 }
2225 c->index.subs[c->index.len++] = c->private;
2226
2227 /*
2228 * Insert ordered
2229 */
2230 TAILQ_FOREACH(p, &community_list, link) {
2231 if (asn_compare_oid(&p->index, &c->index) > 0) {
2232 TAILQ_INSERT_BEFORE(p, c, link);
2233 break;
2234 }
2235 }
2236 if (p == NULL)
2237 TAILQ_INSERT_TAIL(&community_list, c, link);
2238 return (c->value);
2239}
2240
2241const char *
2242comm_string(u_int ncomm)
2243{
2244 struct community *p;
2245
2246 TAILQ_FOREACH(p, &community_list, link)
2247 if (p->value == ncomm)
2248 return (p->string);
2249 return (NULL);
2250}
2251
2252/*
2253 * Delete all communities allocated by a module
2254 */
2255static void
2256comm_flush(struct lmodule *mod)
2257{
2258 struct community *p, *p1;
2259
2260 p = TAILQ_FIRST(&community_list);
2261 while (p != NULL) {
2262 p1 = TAILQ_NEXT(p, link);
2263 if (p->owner == mod) {
2264 free(p->string);
2265 TAILQ_REMOVE(&community_list, p, link);
2266 free(p);
2267 }
2268 p = p1;
2269 }
2270}
2271
2272/*
2273 * Request ID handling.
2274 *
2275 * Allocate a new range of request ids. Use a first fit algorithm.
2276 */
2277u_int
2278reqid_allocate(int size, struct lmodule *mod)
2279{
2280 u_int type;
2281 struct idrange *r, *r1;
2282
2283 if (size <= 0 || size > INT32_MAX) {
2284 syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size);
2285 return (0);
2286 }
2287 /* allocate a type id */
2288 do {
2289 if ((type = next_idrange++) == UINT_MAX)
2290 next_idrange = 1;
2291 TAILQ_FOREACH(r, &idrange_list, link)
2292 if (r->type == type)
2293 break;
2294 } while(r != NULL);
2295
2296 /* find a range */
2297 if (TAILQ_EMPTY(&idrange_list))
2298 r = NULL;
2299 else {
2300 r = TAILQ_FIRST(&idrange_list);
2301 if (r->base < size) {
2302 while((r1 = TAILQ_NEXT(r, link)) != NULL) {
2303 if (r1->base - (r->base + r->size) >= size)
2304 break;
2305 r = r1;
2306 }
2307 r = r1;
2308 }
2309 if (r == NULL) {
2310 r1 = TAILQ_LAST(&idrange_list, idrange_list);
2311 if (INT32_MAX - size + 1 < r1->base + r1->size) {
2312 syslog(LOG_ERR, "out of id ranges (%u)", size);
2313 return (0);
2314 }
2315 }
2316 }
2317
2318 /* allocate structure */
2319 if ((r1 = malloc(sizeof(struct idrange))) == NULL) {
2320 syslog(LOG_ERR, "%s: %m", __FUNCTION__);
2321 return (0);
2322 }
2323
2324 r1->type = type;
2325 r1->size = size;
2326 r1->owner = mod;
2327 if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) {
2328 r1->base = 0;
2329 TAILQ_INSERT_HEAD(&idrange_list, r1, link);
2330 } else if (r == NULL) {
2331 r = TAILQ_LAST(&idrange_list, idrange_list);
2332 r1->base = r->base + r->size;
2333 TAILQ_INSERT_TAIL(&idrange_list, r1, link);
2334 } else {
2335 r = TAILQ_PREV(r, idrange_list, link);
2336 r1->base = r->base + r->size;
2337 TAILQ_INSERT_AFTER(&idrange_list, r, r1, link);
2338 }
2339 r1->next = r1->base;
2340
2341 return (type);
2342}
2343
2344int32_t
2345reqid_next(u_int type)
2346{
2347 struct idrange *r;
2348 int32_t id;
2349
2350 TAILQ_FOREACH(r, &idrange_list, link)
2351 if (r->type == type)
2352 break;
2353 if (r == NULL) {
2354 syslog(LOG_CRIT, "wrong idrange type");
2355 abort();
2356 }
2357 if ((id = r->next++) == r->base + (r->size - 1))
2358 r->next = r->base;
2359 return (id);
2360}
2361
2362int32_t
2363reqid_base(u_int type)
2364{
2365 struct idrange *r;
2366
2367 TAILQ_FOREACH(r, &idrange_list, link)
2368 if (r->type == type)
2369 return (r->base);
2370 syslog(LOG_CRIT, "wrong idrange type");
2371 abort();
2372}
2373
2374u_int
2375reqid_type(int32_t reqid)
2376{
2377 struct idrange *r;
2378
2379 TAILQ_FOREACH(r, &idrange_list, link)
2380 if (reqid >= r->base && reqid <= r->base + (r->size - 1))
2381 return (r->type);
2382 return (0);
2383}
2384
2385int
2386reqid_istype(int32_t reqid, u_int type)
2387{
2388 return (reqid_type(reqid) == type);
2389}
2390
2391/*
2392 * Delete all communities allocated by a module
2393 */
2394static void
2395reqid_flush(struct lmodule *mod)
2396{
2397 struct idrange *p, *p1;
2398
2399 p = TAILQ_FIRST(&idrange_list);
2400 while (p != NULL) {
2401 p1 = TAILQ_NEXT(p, link);
2402 if (p->owner == mod) {
2403 TAILQ_REMOVE(&idrange_list, p, link);
2404 free(p);
2405 }
2406 p = p1;
2407 }
2408}
2409
2410/*
2411 * Merge the given tree for the given module into the main tree.
2412 */
2413static int
2414compare_node(const void *v1, const void *v2)
2415{
2416 const struct snmp_node *n1 = v1;
2417 const struct snmp_node *n2 = v2;
2418
2419 return (asn_compare_oid(&n1->oid, &n2->oid));
2420}
2421static int
2422tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod)
2423{
2424 struct snmp_node *xtree;
2425 u_int i;
2426
2427 xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize));
2428 if (xtree == NULL) {
2429 syslog(LOG_ERR, "tree_merge: %m");
2430 return (-1);
2431 }
2432 tree = xtree;
2433 memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize);
2434
2435 for (i = 0; i < nsize; i++)
2436 tree[tree_size + i].tree_data = mod;
2437
2438 tree_size += nsize;
2439
2440 qsort(tree, tree_size, sizeof(tree[0]), compare_node);
2441
2442 return (0);
2443}
2444
2445/*
2446 * Remove all nodes belonging to the loadable module
2447 */
2448static void
2449tree_unmerge(struct lmodule *mod)
2450{
2451 u_int s, d;
2452
2453 for(s = d = 0; s < tree_size; s++)
2454 if (tree[s].tree_data != mod) {
2455 if (s != d)
2456 tree[d] = tree[s];
2457 d++;
2458 }
2459 tree_size = d;
2460}
2461
2462/*
2463 * Loadable modules
2464 */
2465struct lmodule *
2466lm_load(const char *path, const char *section)
2467{
2468 struct lmodule *m;
2469 int err;
2470 int i;
2471 char *av[MAX_MOD_ARGS + 1];
2472 int ac;
2473 u_int u;
2474
2475 if ((m = malloc(sizeof(*m))) == NULL) {
2476 syslog(LOG_ERR, "lm_load: %m");
2477 return (NULL);
2478 }
2479 m->handle = NULL;
2480 m->flags = 0;
2481 strcpy(m->section, section);
2482
2483 if ((m->path = malloc(strlen(path) + 1)) == NULL) {
2484 syslog(LOG_ERR, "lm_load: %m");
2485 goto err;
2486 }
2487 strcpy(m->path, path);
2488
2489 /*
2490 * Make index
2491 */
2492 m->index.subs[0] = strlen(section);
2493 m->index.len = m->index.subs[0] + 1;
2494 for (u = 0; u < m->index.subs[0]; u++)
2495 m->index.subs[u + 1] = section[u];
2496
2497 /*
2498 * Load the object file and locate the config structure
2499 */
2500 if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
2501 syslog(LOG_ERR, "lm_load: open %s", dlerror());
2502 goto err;
2503 }
2504
2505 if ((m->config = dlsym(m->handle, "config")) == NULL) {
2506 syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror());
2507 goto err;
2508 }
2509
2510 /*
2511 * Insert it into the right place
2512 */
2513 INSERT_OBJECT_OID(m, &lmodules);
2514
2515 /* preserve order */
2516 if (community == COMM_INITIALIZE) {
2517 m->flags |= LM_ONSTARTLIST;
2518 TAILQ_INSERT_TAIL(&modules_start, m, start);
2519 }
2520
2521 /*
2522 * make the argument vector.
2523 */
2524 ac = 0;
2525 for (i = 0; i < nprogargs; i++) {
2526 if (strlen(progargs[i]) >= strlen(section) + 1 &&
2527 strncmp(progargs[i], section, strlen(section)) == 0 &&
2528 progargs[i][strlen(section)] == ':') {
2529 if (ac == MAX_MOD_ARGS) {
2530 syslog(LOG_WARNING, "too many arguments for "
2531 "module '%s", section);
2532 break;
2533 }
2534 av[ac++] = &progargs[i][strlen(section)+1];
2535 }
2536 }
2537 av[ac] = NULL;
2538
2539 /*
2540 * Run the initialization function
2541 */
2542 if ((err = (*m->config->init)(m, ac, av)) != 0) {
2543 syslog(LOG_ERR, "lm_load: init failed: %d", err);
2544 TAILQ_REMOVE(&lmodules, m, link);
2545 goto err;
2546 }
2547
2548 return (m);
2549
2550 err:
2551 if ((m->flags & LM_ONSTARTLIST) != 0)
2552 TAILQ_REMOVE(&modules_start, m, start);
2553 if (m->handle)
2554 dlclose(m->handle);
2555 free(m->path);
2556 free(m);
2557 return (NULL);
2558}
2559
2560/*
2561 * Start a module
2562 */
2563void
2564lm_start(struct lmodule *mod)
2565{
2566 const struct lmodule *m;
2567
2568 /*
2569 * Merge tree. If this fails, unload the module.
2570 */
2571 if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) {
2572 lm_unload(mod);
2573 return;
2574 }
2575
2576 /*
2577 * Read configuration
2578 */
2579 if (read_config(config_file, mod)) {
2580 syslog(LOG_ERR, "error in config file");
2581 lm_unload(mod);
2582 return;
2583 }
2584 if (mod->config->start)
2585 (*mod->config->start)();
2586
2587 mod->flags |= LM_STARTED;
2588
2589 /*
2590 * Inform other modules
2591 */
2592 TAILQ_FOREACH(m, &lmodules, link)
2593 if (m->config->loading)
2594 (*m->config->loading)(mod, 1);
2595}
2596
2597
2598/*
2599 * Unload a module.
2600 */
2601void
2602lm_unload(struct lmodule *m)
2603{
2604 int err;
2605 const struct lmodule *mod;
2606
2607 TAILQ_REMOVE(&lmodules, m, link);
2608 if (m->flags & LM_ONSTARTLIST)
2609 TAILQ_REMOVE(&modules_start, m, start);
2610 tree_unmerge(m);
2611
2612 if ((m->flags & LM_STARTED) && m->config->fini &&
2613 (err = (*m->config->fini)()) != 0)
2614 syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err);
2615
2616 comm_flush(m);
2617 reqid_flush(m);
2618 timer_flush(m);
2619 fd_flush(m);
2620
2621 dlclose(m->handle);
2622 free(m->path);
2623
2624 /*
2625 * Inform other modules
2626 */
2627 TAILQ_FOREACH(mod, &lmodules, link)
2628 if (mod->config->loading)
2629 (*mod->config->loading)(m, 0);
2630
2631 free(m);
2632}
2633
2634/*
2635 * Register an object resource and return the index (or 0 on failures)
2636 */
2637u_int
2638or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod)
2639{
2640 struct objres *objres, *or1;
2641 u_int idx;
2642
2643 /* find a free index */
2644 idx = 1;
2645 for (objres = TAILQ_FIRST(&objres_list);
2646 objres != NULL;
2647 objres = TAILQ_NEXT(objres, link)) {
2648 if ((or1 = TAILQ_NEXT(objres, link)) == NULL ||
2649 or1->index > objres->index + 1) {
2650 idx = objres->index + 1;
2651 break;
2652 }
2653 }
2654
2655 if ((objres = malloc(sizeof(*objres))) == NULL)
2656 return (0);
2657
2658 objres->index = idx;
2659 objres->oid = *or;
2660 strlcpy(objres->descr, descr, sizeof(objres->descr));
2661 objres->uptime = (uint32_t)(get_ticks() - start_tick);
2662 objres->module = mod;
2663
2664 INSERT_OBJECT_INT(objres, &objres_list);
2665
2666 systemg.or_last_change = objres->uptime;
2667
2668 return (idx);
2669}
2670
2671void
2672or_unregister(u_int idx)
2673{
2674 struct objres *objres;
2675
2676 TAILQ_FOREACH(objres, &objres_list, link)
2677 if (objres->index == idx) {
2678 TAILQ_REMOVE(&objres_list, objres, link);
2679 free(objres);
2680 return;
2681 }
2682}
2683
2684/*
2685 * RFC 3414 User-based Security Model support
2686 */
2687
2688struct snmpd_usmstat *
2689bsnmpd_get_usm_stats(void)
2690{
2691 return (&snmpd_usmstats);
2692}
2693
2694void
2695bsnmpd_reset_usm_stats(void)
2696{
2697 memset(&snmpd_usmstats, 0, sizeof(&snmpd_usmstats));
2698}
2699
2700struct usm_user *
2701usm_first_user(void)
2702{
2703 return (SLIST_FIRST(&usm_userlist));
2704}
2705
2706struct usm_user *
2707usm_next_user(struct usm_user *uuser)
2708{
2709 if (uuser == NULL)
2710 return (NULL);
2711
2712 return (SLIST_NEXT(uuser, up));
2713}
2714
2715struct usm_user *
2716usm_find_user(uint8_t *engine, uint32_t elen, char *uname)
2717{
2718 struct usm_user *uuser;
2719
2720 SLIST_FOREACH(uuser, &usm_userlist, up)
2721 if (uuser->user_engine_len == elen &&
2722 memcmp(uuser->user_engine_id, engine, elen) == 0 &&
2723 strlen(uuser->suser.sec_name) == strlen(uname) &&
2724 strcmp(uuser->suser.sec_name, uname) == 0)
2725 break;
2726
2727 return (uuser);
2728}
2729
2730static int
2731usm_compare_user(struct usm_user *u1, struct usm_user *u2)
2732{
2733 uint32_t i;
2734
2735 if (u1->user_engine_len < u2->user_engine_len)
2736 return (-1);
2737 if (u1->user_engine_len > u2->user_engine_len)
2738 return (1);
2739
2740 for (i = 0; i < u1->user_engine_len; i++) {
2741 if (u1->user_engine_id[i] < u2->user_engine_id[i])
2742 return (-1);
2743 if (u1->user_engine_id[i] > u2->user_engine_id[i])
2744 return (1);
2745 }
2746
2747 if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name))
2748 return (-1);
2749 if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name))
2750 return (1);
2751
2752 for (i = 0; i < strlen(u1->suser.sec_name); i++) {
2753 if (u1->suser.sec_name[i] < u2->suser.sec_name[i])
2754 return (-1);
2755 if (u1->suser.sec_name[i] > u2->suser.sec_name[i])
2756 return (1);
2757 }
2758
2759 return (0);
2760}
2761
2762struct usm_user *
2763usm_new_user(uint8_t *eid, uint32_t elen, char *uname)
2764{
2765 int cmp;
2766 struct usm_user *uuser, *temp, *prev;
2767
2768 for (uuser = usm_first_user(); uuser != NULL;
2769 (uuser = usm_next_user(uuser))) {
2770 if (uuser->user_engine_len == elen &&
2771 strlen(uname) == strlen(uuser->suser.sec_name) &&
2772 strcmp(uname, uuser->suser.sec_name) == 0 &&
2773 memcmp(eid, uuser->user_engine_id, elen) == 0)
2774 return (NULL);
2775 }
2776
2777 if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL)
2778 return (NULL);
2779
2780 memset(uuser, 0, sizeof(struct usm_user));
2781 strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ);
2782 memcpy(uuser->user_engine_id, eid, elen);
2783 uuser->user_engine_len = elen;
2784
2785 if ((prev = SLIST_FIRST(&usm_userlist)) == NULL ||
2786 usm_compare_user(uuser, prev) < 0) {
2787 SLIST_INSERT_HEAD(&usm_userlist, uuser, up);
2788 return (uuser);
2789 }
2790
2791 SLIST_FOREACH(temp, &usm_userlist, up) {
2792 if ((cmp = usm_compare_user(uuser, temp)) <= 0)
2793 break;
2794 prev = temp;
2795 }
2796
2797 if (temp == NULL || cmp < 0)
2798 SLIST_INSERT_AFTER(prev, uuser, up);
2799 else if (cmp > 0)
2800 SLIST_INSERT_AFTER(temp, uuser, up);
2801 else {
2802 syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name);
2803 free(uuser);
2804 return (NULL);
2805 }
2806
2807 return (uuser);
2808}
2809
2810void
2811usm_delete_user(struct usm_user *uuser)
2812{
2813 SLIST_REMOVE(&usm_userlist, uuser, usm_user, up);
2814 free(uuser);
2815}
2816
2817void
2818usm_flush_users(void)
2819{
2820 struct usm_user *uuser;
2821
2822 while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) {
2823 SLIST_REMOVE_HEAD(&usm_userlist, up);
2824 free(uuser);
2825 }
2826
2827 SLIST_INIT(&usm_userlist);
2828}
2829
2830/*
2831 * RFC 3415 View-based Access Control Model support
2832 */
2833struct vacm_user *
2834vacm_first_user(void)
2835{
2836 return (SLIST_FIRST(&vacm_userlist));
2837}
2838
2839struct vacm_user *
2840vacm_next_user(struct vacm_user *vuser)
2841{
2842 if (vuser == NULL)
2843 return (NULL);
2844
2845 return (SLIST_NEXT(vuser, vvu));
2846}
2847
2848static int
2849vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2)
2850{
2851 uint32_t i;
2852
2853 if (v1->sec_model < v2->sec_model)
2854 return (-1);
2855 if (v1->sec_model > v2->sec_model)
2856 return (1);
2857
2858 if (strlen(v1->secname) < strlen(v2->secname))
2859 return (-1);
2860 if (strlen(v1->secname) > strlen(v2->secname))
2861 return (1);
2862
2863 for (i = 0; i < strlen(v1->secname); i++) {
2864 if (v1->secname[i] < v2->secname[i])
2865 return (-1);
2866 if (v1->secname[i] > v2->secname[i])
2867 return (1);
2868 }
2869
2870 return (0);
2871}
2872
2873struct vacm_user *
2874vacm_new_user(int32_t smodel, char *uname)
2875{
2876 int cmp;
2877 struct vacm_user *user, *temp, *prev;
2878
2879 SLIST_FOREACH(user, &vacm_userlist, vvu)
2880 if (strcmp(uname, user->secname) == 0 &&
2881 smodel == user->sec_model)
2882 return (NULL);
2883
2884 if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL)
2885 return (NULL);
2886
2887 memset(user, 0, sizeof(*user));
2888 user->group = &vacm_default_group;
2889 SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg);
2890 user->sec_model = smodel;
2891 strlcpy(user->secname, uname, sizeof(user->secname));
2892
2893 if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL ||
2894 vacm_compare_user(user, prev) < 0) {
2895 SLIST_INSERT_HEAD(&vacm_userlist, user, vvu);
2896 return (user);
2897 }
2898
2899 SLIST_FOREACH(temp, &vacm_userlist, vvu) {
2900 if ((cmp = vacm_compare_user(user, temp)) <= 0)
2901 break;
2902 prev = temp;
2903 }
2904
2905 if (temp == NULL || cmp < 0)
2906 SLIST_INSERT_AFTER(prev, user, vvu);
2907 else if (cmp > 0)
2908 SLIST_INSERT_AFTER(temp, user, vvu);
2909 else {
2910 syslog(LOG_ERR, "User %s exists", user->secname);
2911 free(user);
2912 return (NULL);
2913 }
2914
2915 return (user);
2916}
2917
2918int
2919vacm_delete_user(struct vacm_user *user)
2920{
2921 if (user->group != NULL && user->group != &vacm_default_group) {
2922 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
2923 if (SLIST_EMPTY(&user->group->group_users)) {
2924 SLIST_REMOVE(&vacm_grouplist, user->group,
2925 vacm_group, vge);
2926 free(user->group);
2927 }
2928 }
2929
2930 SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu);
2931 free(user);
2932
2933 return (0);
2934}
2935
2936int
2937vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len)
2938{
2939 struct vacm_group *group;
2940
2941 if (len >= SNMP_ADM_STR32_SIZ)
2942 return (-1);
2943
2944 SLIST_FOREACH(group, &vacm_grouplist, vge)
2945 if (strlen(group->groupname) == len &&
2946 memcmp(octets, group->groupname, len) == 0)
2947 break;
2948
2949 if (group == NULL) {
2950 if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL)
2951 return (-1);
2952 memset(group, 0, sizeof(*group));
2953 memcpy(group->groupname, octets, len);
2954 group->groupname[len] = '\0';
2955 SLIST_INSERT_HEAD(&vacm_grouplist, group, vge);
2956 }
2957
2958 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
2959 SLIST_INSERT_HEAD(&group->group_users, user, vvg);
2960 user->group = group;
2961
2962 return (0);
2963}
2964
2965void
2966vacm_groups_init(void)
2967{
2968 SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge);
2969}
2970
2971struct vacm_access *
2972vacm_first_access_rule(void)
2973{
2974 return (TAILQ_FIRST(&vacm_accesslist));
2975}
2976
2977struct vacm_access *
2978vacm_next_access_rule(struct vacm_access *acl)
2979{
2980 if (acl == NULL)
2981 return (NULL);
2982
2983 return (TAILQ_NEXT(acl, vva));
2984}
2985
2986static int
2987vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2)
2988{
2989 uint32_t i;
2990
2991 if (strlen(v1->group->groupname) < strlen(v2->group->groupname))
2992 return (-1);
2993 if (strlen(v1->group->groupname) > strlen(v2->group->groupname))
2994 return (1);
2995
2996 for (i = 0; i < strlen(v1->group->groupname); i++) {
2997 if (v1->group->groupname[i] < v2->group->groupname[i])
2998 return (-1);
2999 if (v1->group->groupname[i] > v2->group->groupname[i])
3000 return (1);
3001 }
3002
3003 if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix))
3004 return (-1);
3005 if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix))
3006 return (1);
3007
3008 for (i = 0; i < strlen(v1->ctx_prefix); i++) {
3009 if (v1->ctx_prefix[i] < v2->ctx_prefix[i])
3010 return (-1);
3011 if (v1->ctx_prefix[i] > v2->ctx_prefix[i])
3012 return (1);
3013 }
3014
3015 if (v1->sec_model < v2->sec_model)
3016 return (-1);
3017 if (v1->sec_model > v2->sec_model)
3018 return (1);
3019
3020 if (v1->sec_level < v2->sec_level)
3021 return (-1);
3022 if (v1->sec_level > v2->sec_level)
3023 return (1);
3024
3025 return (0);
3026}
3027
3028struct vacm_access *
3029vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel)
3030{
3031 struct vacm_group *group;
3032 struct vacm_access *acl, *temp;
3033
3034 TAILQ_FOREACH(acl, &vacm_accesslist, vva) {
3035 if (acl->group == NULL)
3036 continue;
3037 if (strcmp(gname, acl->group->groupname) == 0 &&
3038 strcmp(cprefix, acl->ctx_prefix) == 0 &&
3039 acl->sec_model == smodel && acl->sec_level == slevel)
3040 return (NULL);
3041 }
3042
3043 /* Make sure the group exists */
3044 SLIST_FOREACH(group, &vacm_grouplist, vge)
3045 if (strcmp(gname, group->groupname) == 0)
3046 break;
3047
3048 if (group == NULL)
3049 return (NULL);
3050
3051 if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL)
3052 return (NULL);
3053
3054 memset(acl, 0, sizeof(*acl));
3055 acl->group = group;
3056 strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix));
3057 acl->sec_model = smodel;
3058 acl->sec_level = slevel;
3059
3060 if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL ||
3061 vacm_compare_access_rule(acl, temp) < 0) {
3062 TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva);
3063 return (acl);
3064 }
3065
3066 TAILQ_FOREACH(temp, &vacm_accesslist, vva)
3067 if (vacm_compare_access_rule(acl, temp) < 0) {
3068 TAILQ_INSERT_BEFORE(temp, acl, vva);
3069 return (acl);
3070 }
3071
3072 TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva);
3073
3074 return (acl);
3075}
3076
3077int
3078vacm_delete_access_rule(struct vacm_access *acl)
3079{
3080 TAILQ_REMOVE(&vacm_accesslist, acl, vva);
3081 free(acl);
3082
3083 return (0);
3084}
3085
3086struct vacm_view *
3087vacm_first_view(void)
3088{
3089 return (SLIST_FIRST(&vacm_viewlist));
3090}
3091
3092struct vacm_view *
3093vacm_next_view(struct vacm_view *view)
3094{
3095 if (view == NULL)
3096 return (NULL);
3097
3098 return (SLIST_NEXT(view, vvl));
3099}
3100
3101static int
3102vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2)
3103{
3104 uint32_t i;
3105
3106 if (strlen(v1->viewname) < strlen(v2->viewname))
3107 return (-1);
3108 if (strlen(v1->viewname) > strlen(v2->viewname))
3109 return (1);
3110
3111 for (i = 0; i < strlen(v1->viewname); i++) {
3112 if (v1->viewname[i] < v2->viewname[i])
3113 return (-1);
3114 if (v1->viewname[i] > v2->viewname[i])
3115 return (1);
3116 }
3117
3118 return (asn_compare_oid(&v1->subtree, &v2->subtree));
3119}
3120
3121struct vacm_view *
3122vacm_new_view(char *vname, struct asn_oid *oid)
3123{
3124 int cmp;
3125 struct vacm_view *view, *temp, *prev;
3126
3127 SLIST_FOREACH(view, &vacm_viewlist, vvl)
3128 if (strcmp(vname, view->viewname) == 0)
3129 return (NULL);
3130
3131 if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL)
3132 return (NULL);
3133
3134 memset(view, 0, sizeof(*view));
3135 strlcpy(view->viewname, vname, sizeof(view->viewname));
3136 asn_append_oid(&view->subtree, oid);
3137
3138 if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL ||
3139 vacm_compare_view(view, prev) < 0) {
3140 SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl);
3141 return (view);
3142 }
3143
3144 SLIST_FOREACH(temp, &vacm_viewlist, vvl) {
3145 if ((cmp = vacm_compare_view(view, temp)) <= 0)
3146 break;
3147 prev = temp;
3148 }
3149
3150 if (temp == NULL || cmp < 0)
3151 SLIST_INSERT_AFTER(prev, view, vvl);
3152 else if (cmp > 0)
3153 SLIST_INSERT_AFTER(temp, view, vvl);
3154 else {
3155 syslog(LOG_ERR, "View %s exists", view->viewname);
3156 free(view);
3157 return (NULL);
3158 }
3159
3160 return (view);
3161}
3162
3163int
3164vacm_delete_view(struct vacm_view *view)
3165{
3166 SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl);
3167 free(view);
3168
3169 return (0);
3170}
3171
3172struct vacm_context *
3173vacm_first_context(void)
3174{
3175 return (SLIST_FIRST(&vacm_contextlist));
3176}
3177
3178struct vacm_context *
3179vacm_next_context(struct vacm_context *vacmctx)
3180{
3181 if (vacmctx == NULL)
3182 return (NULL);
3183
3184 return (SLIST_NEXT(vacmctx, vcl));
3185}
3186
3187struct vacm_context *
3188vacm_add_context(char *ctxname, int regid)
3189{
3190 int cmp;
3191 struct vacm_context *ctx, *temp, *prev;
3192
3193 SLIST_FOREACH(ctx, &vacm_contextlist, vcl)
3194 if (strcmp(ctxname, ctx->ctxname) == 0) {
3195 syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
3196 return (NULL);
3197 }
3198
3199 if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL)
3200 return (NULL);
3201
3202 memset(ctx, 0, sizeof(*ctx));
3203 strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname));
3204 ctx->regid = regid;
3205
3206 if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL ||
3207 strlen(ctx->ctxname) < strlen(prev->ctxname) ||
3208 strcmp(ctx->ctxname, prev->ctxname) < 0) {
3209 SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl);
3210 return (ctx);
3211 }
3212
3213 SLIST_FOREACH(temp, &vacm_contextlist, vcl) {
3214 if (strlen(ctx->ctxname) < strlen(temp->ctxname) ||
3215 strcmp(ctx->ctxname, temp->ctxname) < 0) {
3216 cmp = -1;
3217 break;
3218 }
3219 prev = temp;
3220 }
3221
3222 if (temp == NULL || cmp < 0)
3223 SLIST_INSERT_AFTER(prev, ctx, vcl);
3224 else if (cmp > 0)
3225 SLIST_INSERT_AFTER(temp, ctx, vcl);
3226 else {
3227 syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
3228 free(ctx);
3229 return (NULL);
3230 }
3231
3232 return (ctx);
3233}
3234
3235void
3236vacm_flush_contexts(int regid)
3237{
3238 struct vacm_context *ctx, *temp;
3239
3240 SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp)
3241 if (ctx->regid == regid) {
3242 SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl);
3243 free(ctx);
3244 }
3245}
1857 for (;;) {
1858#ifndef USE_LIBBEGEMOT
1859 evEvent event;
1860#endif
1861 struct lmodule *mod;
1862
1863 TAILQ_FOREACH(mod, &lmodules, link)
1864 if (mod->config->idle != NULL)
1865 (*mod->config->idle)();
1866
1867#ifndef USE_LIBBEGEMOT
1868 if (evGetNext(evctx, &event, EV_WAIT) == 0) {
1869 if (evDispatch(evctx, event))
1870 syslog(LOG_ERR, "evDispatch: %m");
1871 } else if (errno != EINTR) {
1872 syslog(LOG_ERR, "evGetNext: %m");
1873 exit(1);
1874 }
1875#else
1876 poll_dispatch(1);
1877#endif
1878
1879 if (work != 0) {
1880 block_sigs();
1881 if (work & WORK_DOINFO) {
1882#ifdef USE_LIBBEGEMOT
1883 info_func();
1884#else
1885 if (evWaitFor(evctx, &work, info_func,
1886 NULL, NULL) == -1) {
1887 syslog(LOG_ERR, "evWaitFor: %m");
1888 exit(1);
1889 }
1890#endif
1891 }
1892 if (work & WORK_RECONFIG) {
1893#ifdef USE_LIBBEGEMOT
1894 config_func();
1895#else
1896 if (evWaitFor(evctx, &work, config_func,
1897 NULL, NULL) == -1) {
1898 syslog(LOG_ERR, "evWaitFor: %m");
1899 exit(1);
1900 }
1901#endif
1902 }
1903 work = 0;
1904 unblock_sigs();
1905#ifndef USE_LIBBEGEMOT
1906 if (evDo(evctx, &work) == -1) {
1907 syslog(LOG_ERR, "evDo: %m");
1908 exit(1);
1909 }
1910#endif
1911 }
1912 }
1913
1914 return (0);
1915}
1916
1917uint64_t
1918get_ticks(void)
1919{
1920 struct timeval tv;
1921 uint64_t ret;
1922
1923 if (gettimeofday(&tv, NULL))
1924 abort();
1925 ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL;
1926 return (ret);
1927}
1928
1929/*
1930 * Timer support
1931 */
1932
1933/*
1934 * Trampoline for the non-repeatable timers.
1935 */
1936#ifdef USE_LIBBEGEMOT
1937static void
1938tfunc(int tid __unused, void *uap)
1939#else
1940static void
1941tfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
1942 struct timespec inter __unused)
1943#endif
1944{
1945 struct timer *tp = uap;
1946
1947 LIST_REMOVE(tp, link);
1948 tp->func(tp->udata);
1949 free(tp);
1950}
1951
1952/*
1953 * Trampoline for the repeatable timers.
1954 */
1955#ifdef USE_LIBBEGEMOT
1956static void
1957trfunc(int tid __unused, void *uap)
1958#else
1959static void
1960trfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
1961 struct timespec inter __unused)
1962#endif
1963{
1964 struct timer *tp = uap;
1965
1966 tp->func(tp->udata);
1967}
1968
1969/*
1970 * Start a one-shot timer
1971 */
1972void *
1973timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod)
1974{
1975 struct timer *tp;
1976#ifndef USE_LIBBEGEMOT
1977 struct timespec due;
1978#endif
1979
1980 if ((tp = malloc(sizeof(struct timer))) == NULL) {
1981 syslog(LOG_CRIT, "out of memory for timer");
1982 exit(1);
1983 }
1984
1985#ifndef USE_LIBBEGEMOT
1986 due = evAddTime(evNowTime(),
1987 evConsTime(ticks / 100, (ticks % 100) * 10000));
1988#endif
1989
1990 tp->udata = udata;
1991 tp->owner = mod;
1992 tp->func = func;
1993
1994 LIST_INSERT_HEAD(&timer_list, tp, link);
1995
1996#ifdef USE_LIBBEGEMOT
1997 if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) {
1998 syslog(LOG_ERR, "cannot set timer: %m");
1999 exit(1);
2000 }
2001#else
2002 if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id)
2003 == -1) {
2004 syslog(LOG_ERR, "cannot set timer: %m");
2005 exit(1);
2006 }
2007#endif
2008 return (tp);
2009}
2010
2011/*
2012 * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument
2013 * is currently ignored and the initial number of ticks is set to the
2014 * repeat number of ticks.
2015 */
2016void *
2017timer_start_repeat(u_int ticks __unused, u_int repeat_ticks,
2018 void (*func)(void *), void *udata, struct lmodule *mod)
2019{
2020 struct timer *tp;
2021#ifndef USE_LIBBEGEMOT
2022 struct timespec due;
2023 struct timespec inter;
2024#endif
2025
2026 if ((tp = malloc(sizeof(struct timer))) == NULL) {
2027 syslog(LOG_CRIT, "out of memory for timer");
2028 exit(1);
2029 }
2030
2031#ifndef USE_LIBBEGEMOT
2032 due = evAddTime(evNowTime(),
2033 evConsTime(ticks / 100, (ticks % 100) * 10000));
2034 inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000);
2035#endif
2036
2037 tp->udata = udata;
2038 tp->owner = mod;
2039 tp->func = func;
2040
2041 LIST_INSERT_HEAD(&timer_list, tp, link);
2042
2043#ifdef USE_LIBBEGEMOT
2044 if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) {
2045 syslog(LOG_ERR, "cannot set timer: %m");
2046 exit(1);
2047 }
2048#else
2049 if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) {
2050 syslog(LOG_ERR, "cannot set timer: %m");
2051 exit(1);
2052 }
2053#endif
2054 return (tp);
2055}
2056
2057/*
2058 * Stop a timer.
2059 */
2060void
2061timer_stop(void *p)
2062{
2063 struct timer *tp = p;
2064
2065 LIST_REMOVE(tp, link);
2066#ifdef USE_LIBBEGEMOT
2067 poll_stop_timer(tp->id);
2068#else
2069 if (evClearTimer(evctx, tp->id) == -1) {
2070 syslog(LOG_ERR, "cannot stop timer: %m");
2071 exit(1);
2072 }
2073#endif
2074 free(p);
2075}
2076
2077static void
2078timer_flush(struct lmodule *mod)
2079{
2080 struct timer *t, *t1;
2081
2082 t = LIST_FIRST(&timer_list);
2083 while (t != NULL) {
2084 t1 = LIST_NEXT(t, link);
2085 if (t->owner == mod)
2086 timer_stop(t);
2087 t = t1;
2088 }
2089}
2090
2091static void
2092snmp_printf_func(const char *fmt, ...)
2093{
2094 va_list ap;
2095 static char *pend = NULL;
2096 char *ret, *new;
2097
2098 va_start(ap, fmt);
2099 vasprintf(&ret, fmt, ap);
2100 va_end(ap);
2101
2102 if (ret == NULL)
2103 return;
2104 if (pend != NULL) {
2105 if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1))
2106 == NULL) {
2107 free(ret);
2108 return;
2109 }
2110 pend = new;
2111 strcat(pend, ret);
2112 free(ret);
2113 } else
2114 pend = ret;
2115
2116 while ((ret = strchr(pend, '\n')) != NULL) {
2117 *ret = '\0';
2118 syslog(LOG_DEBUG, "%s", pend);
2119 if (strlen(ret + 1) == 0) {
2120 free(pend);
2121 pend = NULL;
2122 break;
2123 }
2124 strcpy(pend, ret + 1);
2125 }
2126}
2127
2128static void
2129snmp_error_func(const char *err, ...)
2130{
2131 char errbuf[1000];
2132 va_list ap;
2133
2134 if (!(snmp_trace & LOG_SNMP_ERRORS))
2135 return;
2136
2137 va_start(ap, err);
2138 snprintf(errbuf, sizeof(errbuf), "SNMP: ");
2139 vsnprintf(errbuf + strlen(errbuf),
2140 sizeof(errbuf) - strlen(errbuf), err, ap);
2141 va_end(ap);
2142
2143 syslog(LOG_ERR, "%s", errbuf);
2144}
2145
2146static void
2147snmp_debug_func(const char *err, ...)
2148{
2149 char errbuf[1000];
2150 va_list ap;
2151
2152 va_start(ap, err);
2153 snprintf(errbuf, sizeof(errbuf), "SNMP: ");
2154 vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
2155 err, ap);
2156 va_end(ap);
2157
2158 syslog(LOG_DEBUG, "%s", errbuf);
2159}
2160
2161static void
2162asn_error_func(const struct asn_buf *b, const char *err, ...)
2163{
2164 char errbuf[1000];
2165 va_list ap;
2166 u_int i;
2167
2168 if (!(snmp_trace & LOG_ASN1_ERRORS))
2169 return;
2170
2171 va_start(ap, err);
2172 snprintf(errbuf, sizeof(errbuf), "ASN.1: ");
2173 vsnprintf(errbuf + strlen(errbuf),
2174 sizeof(errbuf) - strlen(errbuf), err, ap);
2175 va_end(ap);
2176
2177 if (b != NULL) {
2178 snprintf(errbuf + strlen(errbuf),
2179 sizeof(errbuf) - strlen(errbuf), " at");
2180 for (i = 0; b->asn_len > i; i++)
2181 snprintf(errbuf + strlen(errbuf),
2182 sizeof(errbuf) - strlen(errbuf),
2183 " %02x", b->asn_cptr[i]);
2184 }
2185
2186 syslog(LOG_ERR, "%s", errbuf);
2187}
2188
2189/*
2190 * Create a new community
2191 */
2192u_int
2193comm_define(u_int priv, const char *descr, struct lmodule *owner,
2194 const char *str)
2195{
2196 struct community *c, *p;
2197 u_int ncomm;
2198
2199 /* generate an identifier */
2200 do {
2201 if ((ncomm = next_community_index++) == UINT_MAX)
2202 next_community_index = 1;
2203 TAILQ_FOREACH(c, &community_list, link)
2204 if (c->value == ncomm)
2205 break;
2206 } while (c != NULL);
2207
2208 if ((c = malloc(sizeof(struct community))) == NULL) {
2209 syslog(LOG_ERR, "comm_define: %m");
2210 return (0);
2211 }
2212 c->owner = owner;
2213 c->value = ncomm;
2214 c->descr = descr;
2215 c->string = NULL;
2216 c->private = priv;
2217
2218 if (str != NULL) {
2219 if((c->string = malloc(strlen(str)+1)) == NULL) {
2220 free(c);
2221 return (0);
2222 }
2223 strcpy(c->string, str);
2224 }
2225
2226 /* make index */
2227 if (c->owner == NULL) {
2228 c->index.len = 1;
2229 c->index.subs[0] = 0;
2230 } else {
2231 c->index = c->owner->index;
2232 }
2233 c->index.subs[c->index.len++] = c->private;
2234
2235 /*
2236 * Insert ordered
2237 */
2238 TAILQ_FOREACH(p, &community_list, link) {
2239 if (asn_compare_oid(&p->index, &c->index) > 0) {
2240 TAILQ_INSERT_BEFORE(p, c, link);
2241 break;
2242 }
2243 }
2244 if (p == NULL)
2245 TAILQ_INSERT_TAIL(&community_list, c, link);
2246 return (c->value);
2247}
2248
2249const char *
2250comm_string(u_int ncomm)
2251{
2252 struct community *p;
2253
2254 TAILQ_FOREACH(p, &community_list, link)
2255 if (p->value == ncomm)
2256 return (p->string);
2257 return (NULL);
2258}
2259
2260/*
2261 * Delete all communities allocated by a module
2262 */
2263static void
2264comm_flush(struct lmodule *mod)
2265{
2266 struct community *p, *p1;
2267
2268 p = TAILQ_FIRST(&community_list);
2269 while (p != NULL) {
2270 p1 = TAILQ_NEXT(p, link);
2271 if (p->owner == mod) {
2272 free(p->string);
2273 TAILQ_REMOVE(&community_list, p, link);
2274 free(p);
2275 }
2276 p = p1;
2277 }
2278}
2279
2280/*
2281 * Request ID handling.
2282 *
2283 * Allocate a new range of request ids. Use a first fit algorithm.
2284 */
2285u_int
2286reqid_allocate(int size, struct lmodule *mod)
2287{
2288 u_int type;
2289 struct idrange *r, *r1;
2290
2291 if (size <= 0 || size > INT32_MAX) {
2292 syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size);
2293 return (0);
2294 }
2295 /* allocate a type id */
2296 do {
2297 if ((type = next_idrange++) == UINT_MAX)
2298 next_idrange = 1;
2299 TAILQ_FOREACH(r, &idrange_list, link)
2300 if (r->type == type)
2301 break;
2302 } while(r != NULL);
2303
2304 /* find a range */
2305 if (TAILQ_EMPTY(&idrange_list))
2306 r = NULL;
2307 else {
2308 r = TAILQ_FIRST(&idrange_list);
2309 if (r->base < size) {
2310 while((r1 = TAILQ_NEXT(r, link)) != NULL) {
2311 if (r1->base - (r->base + r->size) >= size)
2312 break;
2313 r = r1;
2314 }
2315 r = r1;
2316 }
2317 if (r == NULL) {
2318 r1 = TAILQ_LAST(&idrange_list, idrange_list);
2319 if (INT32_MAX - size + 1 < r1->base + r1->size) {
2320 syslog(LOG_ERR, "out of id ranges (%u)", size);
2321 return (0);
2322 }
2323 }
2324 }
2325
2326 /* allocate structure */
2327 if ((r1 = malloc(sizeof(struct idrange))) == NULL) {
2328 syslog(LOG_ERR, "%s: %m", __FUNCTION__);
2329 return (0);
2330 }
2331
2332 r1->type = type;
2333 r1->size = size;
2334 r1->owner = mod;
2335 if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) {
2336 r1->base = 0;
2337 TAILQ_INSERT_HEAD(&idrange_list, r1, link);
2338 } else if (r == NULL) {
2339 r = TAILQ_LAST(&idrange_list, idrange_list);
2340 r1->base = r->base + r->size;
2341 TAILQ_INSERT_TAIL(&idrange_list, r1, link);
2342 } else {
2343 r = TAILQ_PREV(r, idrange_list, link);
2344 r1->base = r->base + r->size;
2345 TAILQ_INSERT_AFTER(&idrange_list, r, r1, link);
2346 }
2347 r1->next = r1->base;
2348
2349 return (type);
2350}
2351
2352int32_t
2353reqid_next(u_int type)
2354{
2355 struct idrange *r;
2356 int32_t id;
2357
2358 TAILQ_FOREACH(r, &idrange_list, link)
2359 if (r->type == type)
2360 break;
2361 if (r == NULL) {
2362 syslog(LOG_CRIT, "wrong idrange type");
2363 abort();
2364 }
2365 if ((id = r->next++) == r->base + (r->size - 1))
2366 r->next = r->base;
2367 return (id);
2368}
2369
2370int32_t
2371reqid_base(u_int type)
2372{
2373 struct idrange *r;
2374
2375 TAILQ_FOREACH(r, &idrange_list, link)
2376 if (r->type == type)
2377 return (r->base);
2378 syslog(LOG_CRIT, "wrong idrange type");
2379 abort();
2380}
2381
2382u_int
2383reqid_type(int32_t reqid)
2384{
2385 struct idrange *r;
2386
2387 TAILQ_FOREACH(r, &idrange_list, link)
2388 if (reqid >= r->base && reqid <= r->base + (r->size - 1))
2389 return (r->type);
2390 return (0);
2391}
2392
2393int
2394reqid_istype(int32_t reqid, u_int type)
2395{
2396 return (reqid_type(reqid) == type);
2397}
2398
2399/*
2400 * Delete all communities allocated by a module
2401 */
2402static void
2403reqid_flush(struct lmodule *mod)
2404{
2405 struct idrange *p, *p1;
2406
2407 p = TAILQ_FIRST(&idrange_list);
2408 while (p != NULL) {
2409 p1 = TAILQ_NEXT(p, link);
2410 if (p->owner == mod) {
2411 TAILQ_REMOVE(&idrange_list, p, link);
2412 free(p);
2413 }
2414 p = p1;
2415 }
2416}
2417
2418/*
2419 * Merge the given tree for the given module into the main tree.
2420 */
2421static int
2422compare_node(const void *v1, const void *v2)
2423{
2424 const struct snmp_node *n1 = v1;
2425 const struct snmp_node *n2 = v2;
2426
2427 return (asn_compare_oid(&n1->oid, &n2->oid));
2428}
2429static int
2430tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod)
2431{
2432 struct snmp_node *xtree;
2433 u_int i;
2434
2435 xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize));
2436 if (xtree == NULL) {
2437 syslog(LOG_ERR, "tree_merge: %m");
2438 return (-1);
2439 }
2440 tree = xtree;
2441 memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize);
2442
2443 for (i = 0; i < nsize; i++)
2444 tree[tree_size + i].tree_data = mod;
2445
2446 tree_size += nsize;
2447
2448 qsort(tree, tree_size, sizeof(tree[0]), compare_node);
2449
2450 return (0);
2451}
2452
2453/*
2454 * Remove all nodes belonging to the loadable module
2455 */
2456static void
2457tree_unmerge(struct lmodule *mod)
2458{
2459 u_int s, d;
2460
2461 for(s = d = 0; s < tree_size; s++)
2462 if (tree[s].tree_data != mod) {
2463 if (s != d)
2464 tree[d] = tree[s];
2465 d++;
2466 }
2467 tree_size = d;
2468}
2469
2470/*
2471 * Loadable modules
2472 */
2473struct lmodule *
2474lm_load(const char *path, const char *section)
2475{
2476 struct lmodule *m;
2477 int err;
2478 int i;
2479 char *av[MAX_MOD_ARGS + 1];
2480 int ac;
2481 u_int u;
2482
2483 if ((m = malloc(sizeof(*m))) == NULL) {
2484 syslog(LOG_ERR, "lm_load: %m");
2485 return (NULL);
2486 }
2487 m->handle = NULL;
2488 m->flags = 0;
2489 strcpy(m->section, section);
2490
2491 if ((m->path = malloc(strlen(path) + 1)) == NULL) {
2492 syslog(LOG_ERR, "lm_load: %m");
2493 goto err;
2494 }
2495 strcpy(m->path, path);
2496
2497 /*
2498 * Make index
2499 */
2500 m->index.subs[0] = strlen(section);
2501 m->index.len = m->index.subs[0] + 1;
2502 for (u = 0; u < m->index.subs[0]; u++)
2503 m->index.subs[u + 1] = section[u];
2504
2505 /*
2506 * Load the object file and locate the config structure
2507 */
2508 if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
2509 syslog(LOG_ERR, "lm_load: open %s", dlerror());
2510 goto err;
2511 }
2512
2513 if ((m->config = dlsym(m->handle, "config")) == NULL) {
2514 syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror());
2515 goto err;
2516 }
2517
2518 /*
2519 * Insert it into the right place
2520 */
2521 INSERT_OBJECT_OID(m, &lmodules);
2522
2523 /* preserve order */
2524 if (community == COMM_INITIALIZE) {
2525 m->flags |= LM_ONSTARTLIST;
2526 TAILQ_INSERT_TAIL(&modules_start, m, start);
2527 }
2528
2529 /*
2530 * make the argument vector.
2531 */
2532 ac = 0;
2533 for (i = 0; i < nprogargs; i++) {
2534 if (strlen(progargs[i]) >= strlen(section) + 1 &&
2535 strncmp(progargs[i], section, strlen(section)) == 0 &&
2536 progargs[i][strlen(section)] == ':') {
2537 if (ac == MAX_MOD_ARGS) {
2538 syslog(LOG_WARNING, "too many arguments for "
2539 "module '%s", section);
2540 break;
2541 }
2542 av[ac++] = &progargs[i][strlen(section)+1];
2543 }
2544 }
2545 av[ac] = NULL;
2546
2547 /*
2548 * Run the initialization function
2549 */
2550 if ((err = (*m->config->init)(m, ac, av)) != 0) {
2551 syslog(LOG_ERR, "lm_load: init failed: %d", err);
2552 TAILQ_REMOVE(&lmodules, m, link);
2553 goto err;
2554 }
2555
2556 return (m);
2557
2558 err:
2559 if ((m->flags & LM_ONSTARTLIST) != 0)
2560 TAILQ_REMOVE(&modules_start, m, start);
2561 if (m->handle)
2562 dlclose(m->handle);
2563 free(m->path);
2564 free(m);
2565 return (NULL);
2566}
2567
2568/*
2569 * Start a module
2570 */
2571void
2572lm_start(struct lmodule *mod)
2573{
2574 const struct lmodule *m;
2575
2576 /*
2577 * Merge tree. If this fails, unload the module.
2578 */
2579 if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) {
2580 lm_unload(mod);
2581 return;
2582 }
2583
2584 /*
2585 * Read configuration
2586 */
2587 if (read_config(config_file, mod)) {
2588 syslog(LOG_ERR, "error in config file");
2589 lm_unload(mod);
2590 return;
2591 }
2592 if (mod->config->start)
2593 (*mod->config->start)();
2594
2595 mod->flags |= LM_STARTED;
2596
2597 /*
2598 * Inform other modules
2599 */
2600 TAILQ_FOREACH(m, &lmodules, link)
2601 if (m->config->loading)
2602 (*m->config->loading)(mod, 1);
2603}
2604
2605
2606/*
2607 * Unload a module.
2608 */
2609void
2610lm_unload(struct lmodule *m)
2611{
2612 int err;
2613 const struct lmodule *mod;
2614
2615 TAILQ_REMOVE(&lmodules, m, link);
2616 if (m->flags & LM_ONSTARTLIST)
2617 TAILQ_REMOVE(&modules_start, m, start);
2618 tree_unmerge(m);
2619
2620 if ((m->flags & LM_STARTED) && m->config->fini &&
2621 (err = (*m->config->fini)()) != 0)
2622 syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err);
2623
2624 comm_flush(m);
2625 reqid_flush(m);
2626 timer_flush(m);
2627 fd_flush(m);
2628
2629 dlclose(m->handle);
2630 free(m->path);
2631
2632 /*
2633 * Inform other modules
2634 */
2635 TAILQ_FOREACH(mod, &lmodules, link)
2636 if (mod->config->loading)
2637 (*mod->config->loading)(m, 0);
2638
2639 free(m);
2640}
2641
2642/*
2643 * Register an object resource and return the index (or 0 on failures)
2644 */
2645u_int
2646or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod)
2647{
2648 struct objres *objres, *or1;
2649 u_int idx;
2650
2651 /* find a free index */
2652 idx = 1;
2653 for (objres = TAILQ_FIRST(&objres_list);
2654 objres != NULL;
2655 objres = TAILQ_NEXT(objres, link)) {
2656 if ((or1 = TAILQ_NEXT(objres, link)) == NULL ||
2657 or1->index > objres->index + 1) {
2658 idx = objres->index + 1;
2659 break;
2660 }
2661 }
2662
2663 if ((objres = malloc(sizeof(*objres))) == NULL)
2664 return (0);
2665
2666 objres->index = idx;
2667 objres->oid = *or;
2668 strlcpy(objres->descr, descr, sizeof(objres->descr));
2669 objres->uptime = (uint32_t)(get_ticks() - start_tick);
2670 objres->module = mod;
2671
2672 INSERT_OBJECT_INT(objres, &objres_list);
2673
2674 systemg.or_last_change = objres->uptime;
2675
2676 return (idx);
2677}
2678
2679void
2680or_unregister(u_int idx)
2681{
2682 struct objres *objres;
2683
2684 TAILQ_FOREACH(objres, &objres_list, link)
2685 if (objres->index == idx) {
2686 TAILQ_REMOVE(&objres_list, objres, link);
2687 free(objres);
2688 return;
2689 }
2690}
2691
2692/*
2693 * RFC 3414 User-based Security Model support
2694 */
2695
2696struct snmpd_usmstat *
2697bsnmpd_get_usm_stats(void)
2698{
2699 return (&snmpd_usmstats);
2700}
2701
2702void
2703bsnmpd_reset_usm_stats(void)
2704{
2705 memset(&snmpd_usmstats, 0, sizeof(&snmpd_usmstats));
2706}
2707
2708struct usm_user *
2709usm_first_user(void)
2710{
2711 return (SLIST_FIRST(&usm_userlist));
2712}
2713
2714struct usm_user *
2715usm_next_user(struct usm_user *uuser)
2716{
2717 if (uuser == NULL)
2718 return (NULL);
2719
2720 return (SLIST_NEXT(uuser, up));
2721}
2722
2723struct usm_user *
2724usm_find_user(uint8_t *engine, uint32_t elen, char *uname)
2725{
2726 struct usm_user *uuser;
2727
2728 SLIST_FOREACH(uuser, &usm_userlist, up)
2729 if (uuser->user_engine_len == elen &&
2730 memcmp(uuser->user_engine_id, engine, elen) == 0 &&
2731 strlen(uuser->suser.sec_name) == strlen(uname) &&
2732 strcmp(uuser->suser.sec_name, uname) == 0)
2733 break;
2734
2735 return (uuser);
2736}
2737
2738static int
2739usm_compare_user(struct usm_user *u1, struct usm_user *u2)
2740{
2741 uint32_t i;
2742
2743 if (u1->user_engine_len < u2->user_engine_len)
2744 return (-1);
2745 if (u1->user_engine_len > u2->user_engine_len)
2746 return (1);
2747
2748 for (i = 0; i < u1->user_engine_len; i++) {
2749 if (u1->user_engine_id[i] < u2->user_engine_id[i])
2750 return (-1);
2751 if (u1->user_engine_id[i] > u2->user_engine_id[i])
2752 return (1);
2753 }
2754
2755 if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name))
2756 return (-1);
2757 if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name))
2758 return (1);
2759
2760 for (i = 0; i < strlen(u1->suser.sec_name); i++) {
2761 if (u1->suser.sec_name[i] < u2->suser.sec_name[i])
2762 return (-1);
2763 if (u1->suser.sec_name[i] > u2->suser.sec_name[i])
2764 return (1);
2765 }
2766
2767 return (0);
2768}
2769
2770struct usm_user *
2771usm_new_user(uint8_t *eid, uint32_t elen, char *uname)
2772{
2773 int cmp;
2774 struct usm_user *uuser, *temp, *prev;
2775
2776 for (uuser = usm_first_user(); uuser != NULL;
2777 (uuser = usm_next_user(uuser))) {
2778 if (uuser->user_engine_len == elen &&
2779 strlen(uname) == strlen(uuser->suser.sec_name) &&
2780 strcmp(uname, uuser->suser.sec_name) == 0 &&
2781 memcmp(eid, uuser->user_engine_id, elen) == 0)
2782 return (NULL);
2783 }
2784
2785 if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL)
2786 return (NULL);
2787
2788 memset(uuser, 0, sizeof(struct usm_user));
2789 strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ);
2790 memcpy(uuser->user_engine_id, eid, elen);
2791 uuser->user_engine_len = elen;
2792
2793 if ((prev = SLIST_FIRST(&usm_userlist)) == NULL ||
2794 usm_compare_user(uuser, prev) < 0) {
2795 SLIST_INSERT_HEAD(&usm_userlist, uuser, up);
2796 return (uuser);
2797 }
2798
2799 SLIST_FOREACH(temp, &usm_userlist, up) {
2800 if ((cmp = usm_compare_user(uuser, temp)) <= 0)
2801 break;
2802 prev = temp;
2803 }
2804
2805 if (temp == NULL || cmp < 0)
2806 SLIST_INSERT_AFTER(prev, uuser, up);
2807 else if (cmp > 0)
2808 SLIST_INSERT_AFTER(temp, uuser, up);
2809 else {
2810 syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name);
2811 free(uuser);
2812 return (NULL);
2813 }
2814
2815 return (uuser);
2816}
2817
2818void
2819usm_delete_user(struct usm_user *uuser)
2820{
2821 SLIST_REMOVE(&usm_userlist, uuser, usm_user, up);
2822 free(uuser);
2823}
2824
2825void
2826usm_flush_users(void)
2827{
2828 struct usm_user *uuser;
2829
2830 while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) {
2831 SLIST_REMOVE_HEAD(&usm_userlist, up);
2832 free(uuser);
2833 }
2834
2835 SLIST_INIT(&usm_userlist);
2836}
2837
2838/*
2839 * RFC 3415 View-based Access Control Model support
2840 */
2841struct vacm_user *
2842vacm_first_user(void)
2843{
2844 return (SLIST_FIRST(&vacm_userlist));
2845}
2846
2847struct vacm_user *
2848vacm_next_user(struct vacm_user *vuser)
2849{
2850 if (vuser == NULL)
2851 return (NULL);
2852
2853 return (SLIST_NEXT(vuser, vvu));
2854}
2855
2856static int
2857vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2)
2858{
2859 uint32_t i;
2860
2861 if (v1->sec_model < v2->sec_model)
2862 return (-1);
2863 if (v1->sec_model > v2->sec_model)
2864 return (1);
2865
2866 if (strlen(v1->secname) < strlen(v2->secname))
2867 return (-1);
2868 if (strlen(v1->secname) > strlen(v2->secname))
2869 return (1);
2870
2871 for (i = 0; i < strlen(v1->secname); i++) {
2872 if (v1->secname[i] < v2->secname[i])
2873 return (-1);
2874 if (v1->secname[i] > v2->secname[i])
2875 return (1);
2876 }
2877
2878 return (0);
2879}
2880
2881struct vacm_user *
2882vacm_new_user(int32_t smodel, char *uname)
2883{
2884 int cmp;
2885 struct vacm_user *user, *temp, *prev;
2886
2887 SLIST_FOREACH(user, &vacm_userlist, vvu)
2888 if (strcmp(uname, user->secname) == 0 &&
2889 smodel == user->sec_model)
2890 return (NULL);
2891
2892 if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL)
2893 return (NULL);
2894
2895 memset(user, 0, sizeof(*user));
2896 user->group = &vacm_default_group;
2897 SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg);
2898 user->sec_model = smodel;
2899 strlcpy(user->secname, uname, sizeof(user->secname));
2900
2901 if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL ||
2902 vacm_compare_user(user, prev) < 0) {
2903 SLIST_INSERT_HEAD(&vacm_userlist, user, vvu);
2904 return (user);
2905 }
2906
2907 SLIST_FOREACH(temp, &vacm_userlist, vvu) {
2908 if ((cmp = vacm_compare_user(user, temp)) <= 0)
2909 break;
2910 prev = temp;
2911 }
2912
2913 if (temp == NULL || cmp < 0)
2914 SLIST_INSERT_AFTER(prev, user, vvu);
2915 else if (cmp > 0)
2916 SLIST_INSERT_AFTER(temp, user, vvu);
2917 else {
2918 syslog(LOG_ERR, "User %s exists", user->secname);
2919 free(user);
2920 return (NULL);
2921 }
2922
2923 return (user);
2924}
2925
2926int
2927vacm_delete_user(struct vacm_user *user)
2928{
2929 if (user->group != NULL && user->group != &vacm_default_group) {
2930 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
2931 if (SLIST_EMPTY(&user->group->group_users)) {
2932 SLIST_REMOVE(&vacm_grouplist, user->group,
2933 vacm_group, vge);
2934 free(user->group);
2935 }
2936 }
2937
2938 SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu);
2939 free(user);
2940
2941 return (0);
2942}
2943
2944int
2945vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len)
2946{
2947 struct vacm_group *group;
2948
2949 if (len >= SNMP_ADM_STR32_SIZ)
2950 return (-1);
2951
2952 SLIST_FOREACH(group, &vacm_grouplist, vge)
2953 if (strlen(group->groupname) == len &&
2954 memcmp(octets, group->groupname, len) == 0)
2955 break;
2956
2957 if (group == NULL) {
2958 if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL)
2959 return (-1);
2960 memset(group, 0, sizeof(*group));
2961 memcpy(group->groupname, octets, len);
2962 group->groupname[len] = '\0';
2963 SLIST_INSERT_HEAD(&vacm_grouplist, group, vge);
2964 }
2965
2966 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
2967 SLIST_INSERT_HEAD(&group->group_users, user, vvg);
2968 user->group = group;
2969
2970 return (0);
2971}
2972
2973void
2974vacm_groups_init(void)
2975{
2976 SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge);
2977}
2978
2979struct vacm_access *
2980vacm_first_access_rule(void)
2981{
2982 return (TAILQ_FIRST(&vacm_accesslist));
2983}
2984
2985struct vacm_access *
2986vacm_next_access_rule(struct vacm_access *acl)
2987{
2988 if (acl == NULL)
2989 return (NULL);
2990
2991 return (TAILQ_NEXT(acl, vva));
2992}
2993
2994static int
2995vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2)
2996{
2997 uint32_t i;
2998
2999 if (strlen(v1->group->groupname) < strlen(v2->group->groupname))
3000 return (-1);
3001 if (strlen(v1->group->groupname) > strlen(v2->group->groupname))
3002 return (1);
3003
3004 for (i = 0; i < strlen(v1->group->groupname); i++) {
3005 if (v1->group->groupname[i] < v2->group->groupname[i])
3006 return (-1);
3007 if (v1->group->groupname[i] > v2->group->groupname[i])
3008 return (1);
3009 }
3010
3011 if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix))
3012 return (-1);
3013 if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix))
3014 return (1);
3015
3016 for (i = 0; i < strlen(v1->ctx_prefix); i++) {
3017 if (v1->ctx_prefix[i] < v2->ctx_prefix[i])
3018 return (-1);
3019 if (v1->ctx_prefix[i] > v2->ctx_prefix[i])
3020 return (1);
3021 }
3022
3023 if (v1->sec_model < v2->sec_model)
3024 return (-1);
3025 if (v1->sec_model > v2->sec_model)
3026 return (1);
3027
3028 if (v1->sec_level < v2->sec_level)
3029 return (-1);
3030 if (v1->sec_level > v2->sec_level)
3031 return (1);
3032
3033 return (0);
3034}
3035
3036struct vacm_access *
3037vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel)
3038{
3039 struct vacm_group *group;
3040 struct vacm_access *acl, *temp;
3041
3042 TAILQ_FOREACH(acl, &vacm_accesslist, vva) {
3043 if (acl->group == NULL)
3044 continue;
3045 if (strcmp(gname, acl->group->groupname) == 0 &&
3046 strcmp(cprefix, acl->ctx_prefix) == 0 &&
3047 acl->sec_model == smodel && acl->sec_level == slevel)
3048 return (NULL);
3049 }
3050
3051 /* Make sure the group exists */
3052 SLIST_FOREACH(group, &vacm_grouplist, vge)
3053 if (strcmp(gname, group->groupname) == 0)
3054 break;
3055
3056 if (group == NULL)
3057 return (NULL);
3058
3059 if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL)
3060 return (NULL);
3061
3062 memset(acl, 0, sizeof(*acl));
3063 acl->group = group;
3064 strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix));
3065 acl->sec_model = smodel;
3066 acl->sec_level = slevel;
3067
3068 if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL ||
3069 vacm_compare_access_rule(acl, temp) < 0) {
3070 TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva);
3071 return (acl);
3072 }
3073
3074 TAILQ_FOREACH(temp, &vacm_accesslist, vva)
3075 if (vacm_compare_access_rule(acl, temp) < 0) {
3076 TAILQ_INSERT_BEFORE(temp, acl, vva);
3077 return (acl);
3078 }
3079
3080 TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva);
3081
3082 return (acl);
3083}
3084
3085int
3086vacm_delete_access_rule(struct vacm_access *acl)
3087{
3088 TAILQ_REMOVE(&vacm_accesslist, acl, vva);
3089 free(acl);
3090
3091 return (0);
3092}
3093
3094struct vacm_view *
3095vacm_first_view(void)
3096{
3097 return (SLIST_FIRST(&vacm_viewlist));
3098}
3099
3100struct vacm_view *
3101vacm_next_view(struct vacm_view *view)
3102{
3103 if (view == NULL)
3104 return (NULL);
3105
3106 return (SLIST_NEXT(view, vvl));
3107}
3108
3109static int
3110vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2)
3111{
3112 uint32_t i;
3113
3114 if (strlen(v1->viewname) < strlen(v2->viewname))
3115 return (-1);
3116 if (strlen(v1->viewname) > strlen(v2->viewname))
3117 return (1);
3118
3119 for (i = 0; i < strlen(v1->viewname); i++) {
3120 if (v1->viewname[i] < v2->viewname[i])
3121 return (-1);
3122 if (v1->viewname[i] > v2->viewname[i])
3123 return (1);
3124 }
3125
3126 return (asn_compare_oid(&v1->subtree, &v2->subtree));
3127}
3128
3129struct vacm_view *
3130vacm_new_view(char *vname, struct asn_oid *oid)
3131{
3132 int cmp;
3133 struct vacm_view *view, *temp, *prev;
3134
3135 SLIST_FOREACH(view, &vacm_viewlist, vvl)
3136 if (strcmp(vname, view->viewname) == 0)
3137 return (NULL);
3138
3139 if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL)
3140 return (NULL);
3141
3142 memset(view, 0, sizeof(*view));
3143 strlcpy(view->viewname, vname, sizeof(view->viewname));
3144 asn_append_oid(&view->subtree, oid);
3145
3146 if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL ||
3147 vacm_compare_view(view, prev) < 0) {
3148 SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl);
3149 return (view);
3150 }
3151
3152 SLIST_FOREACH(temp, &vacm_viewlist, vvl) {
3153 if ((cmp = vacm_compare_view(view, temp)) <= 0)
3154 break;
3155 prev = temp;
3156 }
3157
3158 if (temp == NULL || cmp < 0)
3159 SLIST_INSERT_AFTER(prev, view, vvl);
3160 else if (cmp > 0)
3161 SLIST_INSERT_AFTER(temp, view, vvl);
3162 else {
3163 syslog(LOG_ERR, "View %s exists", view->viewname);
3164 free(view);
3165 return (NULL);
3166 }
3167
3168 return (view);
3169}
3170
3171int
3172vacm_delete_view(struct vacm_view *view)
3173{
3174 SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl);
3175 free(view);
3176
3177 return (0);
3178}
3179
3180struct vacm_context *
3181vacm_first_context(void)
3182{
3183 return (SLIST_FIRST(&vacm_contextlist));
3184}
3185
3186struct vacm_context *
3187vacm_next_context(struct vacm_context *vacmctx)
3188{
3189 if (vacmctx == NULL)
3190 return (NULL);
3191
3192 return (SLIST_NEXT(vacmctx, vcl));
3193}
3194
3195struct vacm_context *
3196vacm_add_context(char *ctxname, int regid)
3197{
3198 int cmp;
3199 struct vacm_context *ctx, *temp, *prev;
3200
3201 SLIST_FOREACH(ctx, &vacm_contextlist, vcl)
3202 if (strcmp(ctxname, ctx->ctxname) == 0) {
3203 syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
3204 return (NULL);
3205 }
3206
3207 if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL)
3208 return (NULL);
3209
3210 memset(ctx, 0, sizeof(*ctx));
3211 strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname));
3212 ctx->regid = regid;
3213
3214 if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL ||
3215 strlen(ctx->ctxname) < strlen(prev->ctxname) ||
3216 strcmp(ctx->ctxname, prev->ctxname) < 0) {
3217 SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl);
3218 return (ctx);
3219 }
3220
3221 SLIST_FOREACH(temp, &vacm_contextlist, vcl) {
3222 if (strlen(ctx->ctxname) < strlen(temp->ctxname) ||
3223 strcmp(ctx->ctxname, temp->ctxname) < 0) {
3224 cmp = -1;
3225 break;
3226 }
3227 prev = temp;
3228 }
3229
3230 if (temp == NULL || cmp < 0)
3231 SLIST_INSERT_AFTER(prev, ctx, vcl);
3232 else if (cmp > 0)
3233 SLIST_INSERT_AFTER(temp, ctx, vcl);
3234 else {
3235 syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
3236 free(ctx);
3237 return (NULL);
3238 }
3239
3240 return (ctx);
3241}
3242
3243void
3244vacm_flush_contexts(int regid)
3245{
3246 struct vacm_context *ctx, *temp;
3247
3248 SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp)
3249 if (ctx->regid == regid) {
3250 SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl);
3251 free(ctx);
3252 }
3253}