1 /* $OpenBSD: application_agentx.c,v 1.16 2024/02/06 12:44:27 martijn Exp $ */
2 /*
3 * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/queue.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/tree.h>
22 #include <sys/un.h>
23
24 #include <ber.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <inttypes.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "application.h"
35 #include "ax.h"
36 #include "log.h"
37 #include "mib.h"
38 #include "smi.h"
39 #include "snmp.h"
40 #include "snmpd.h"
41
42 #define AGENTX_DEFAULTTIMEOUT 5
43
44 struct appl_agentx_connection {
45 uint32_t conn_id;
46 /*
47 * A backend has several overruling properties:
48 * - If it exits, snmpd crashes
49 * - All registrations are priority 1
50 * - All registrations own the subtree.
51 */
52 int conn_backend;
53 struct ax *conn_ax;
54 struct event conn_rev;
55 struct event conn_wev;
56
57 TAILQ_HEAD(, appl_agentx_session) conn_sessions;
58 RB_ENTRY(appl_agentx_connection) conn_entry;
59 };
60
61 struct appl_agentx_session {
62 uint32_t sess_id;
63 struct appl_agentx_connection *sess_conn;
64 /*
65 * RFC 2741 section 7.1.1:
66 * All subsequent AgentX protocol operations initiated by the master
67 * agent for this session must use this byte ordering and set this bit
68 * accordingly.
69 */
70 enum ax_byte_order sess_byteorder;
71 uint8_t sess_timeout;
72 struct ax_oid sess_oid;
73 struct ax_ostring sess_descr;
74 struct appl_backend sess_backend;
75
76 RB_ENTRY(appl_agentx_session) sess_entry;
77 TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
78 };
79
80 void appl_agentx_listen(struct agentx_master *);
81 void appl_agentx_accept(int, short, void *);
82 void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason);
83 void appl_agentx_recv(int, short, void *);
84 void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
85 void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
86 void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
87 void appl_agentx_session_free(struct appl_agentx_session *);
88 void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
89 void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
90 void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
91 struct appl_varbind *);
92 void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
93 struct appl_varbind *);
94 void appl_agentx_addagentcaps(struct appl_agentx_session *, struct ax_pdu *);
95 void appl_agentx_removeagentcaps(struct appl_agentx_session *, struct ax_pdu *);
96 void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
97 void appl_agentx_send(int, short, void *);
98 struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
99 struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
100 struct ax_ostring *appl_agentx_string2ostring(const char *,
101 struct ax_ostring *);
102 int appl_agentx_cmp(struct appl_agentx_connection *,
103 struct appl_agentx_connection *);
104 int appl_agentx_session_cmp(struct appl_agentx_session *,
105 struct appl_agentx_session *);
106
107 struct appl_backend_functions appl_agentx_functions = {
108 .ab_close = appl_agentx_forceclose,
109 .ab_get = appl_agentx_get,
110 .ab_getnext = appl_agentx_getnext,
111 .ab_getbulk = NULL, /* not properly supported in application.c and libagentx */
112 };
113
114 RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
115 RB_INITIALIZER(&appl_agentx_conns);
116 RB_HEAD(appl_agentx_sessions, appl_agentx_session) appl_agentx_sessions =
117 RB_INITIALIZER(&appl_agentx_sessions);
118
119 RB_PROTOTYPE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
120 appl_agentx_cmp);
121 RB_PROTOTYPE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
122 appl_agentx_session_cmp);
123
124 void
appl_agentx(void)125 appl_agentx(void)
126 {
127 struct agentx_master *master;
128
129 TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry)
130 appl_agentx_listen(master);
131 }
132
133 void
appl_agentx_init(void)134 appl_agentx_init(void)
135 {
136 struct agentx_master *master;
137
138 TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry) {
139 if (master->axm_fd == -1)
140 continue;
141 event_set(&(master->axm_ev), master->axm_fd,
142 EV_READ | EV_PERSIST, appl_agentx_accept, master);
143 event_add(&(master->axm_ev), NULL);
144 log_info("AgentX: listening on %s", master->axm_sun.sun_path);
145 }
146 }
147 void
appl_agentx_listen(struct agentx_master * master)148 appl_agentx_listen(struct agentx_master *master)
149 {
150 mode_t mask;
151
152 unlink(master->axm_sun.sun_path);
153
154 mask = umask(0777);
155 if ((master->axm_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
156 bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
157 sizeof(master->axm_sun)) == -1 ||
158 listen(master->axm_fd, 5)) {
159 log_warn("AgentX: listen %s", master->axm_sun.sun_path);
160 umask(mask);
161 return;
162 }
163 umask(mask);
164 if (chown(master->axm_sun.sun_path, master->axm_owner,
165 master->axm_group) == -1) {
166 log_warn("AgentX: chown %s", master->axm_sun.sun_path);
167 goto fail;
168 }
169 if (chmod(master->axm_sun.sun_path, master->axm_mode) == -1) {
170 log_warn("AgentX: chmod %s", master->axm_sun.sun_path);
171 goto fail;
172 }
173 return;
174 fail:
175 close(master->axm_fd);
176 master->axm_fd = -1;
177 }
178
179 void
appl_agentx_shutdown(void)180 appl_agentx_shutdown(void)
181 {
182 struct appl_agentx_connection *conn, *tconn;
183
184 RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
185 appl_agentx_free(conn, APPL_CLOSE_REASONSHUTDOWN);
186 }
187
188 void
appl_agentx_accept(int masterfd,short event,void * cookie)189 appl_agentx_accept(int masterfd, short event, void *cookie)
190 {
191 int fd;
192 struct agentx_master *master = cookie;
193 struct sockaddr_un sun;
194 socklen_t sunlen = sizeof(sun);
195 struct appl_agentx_connection *conn = NULL;
196
197 if ((fd = accept(masterfd, (struct sockaddr *)&sun, &sunlen)) == -1) {
198 log_warn("AgentX: accept %s", master->axm_sun.sun_path);
199 return;
200 }
201
202 if ((conn = malloc(sizeof(*conn))) == NULL) {
203 log_warn(NULL);
204 goto fail;
205 }
206
207 conn->conn_backend = 0;
208 TAILQ_INIT(&(conn->conn_sessions));
209 if ((conn->conn_ax = ax_new(fd)) == NULL) {
210 log_warn(NULL);
211 goto fail;
212 }
213
214 do {
215 conn->conn_id = arc4random();
216 } while (RB_INSERT(appl_agentx_conns,
217 &appl_agentx_conns, conn) != NULL);
218
219 event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
220 appl_agentx_recv, conn);
221 event_add(&(conn->conn_rev), NULL);
222 event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
223 log_info("AgentX(%"PRIu32"): new connection", conn->conn_id);
224
225 return;
226 fail:
227 close(fd);
228 free(conn);
229 }
230
231 void
appl_agentx_backend(int fd)232 appl_agentx_backend(int fd)
233 {
234 struct appl_agentx_connection *conn;
235
236 if ((conn = malloc(sizeof(*conn))) == NULL)
237 fatal(NULL);
238
239 conn->conn_backend = 1;
240 TAILQ_INIT(&(conn->conn_sessions));
241 if ((conn->conn_ax = ax_new(fd)) == NULL)
242 fatal("ax_new");
243
244 do {
245 conn->conn_id = arc4random();
246 } while (RB_INSERT(appl_agentx_conns,
247 &appl_agentx_conns, conn) != NULL);
248
249 event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
250 appl_agentx_recv, conn);
251 event_add(&(conn->conn_rev), NULL);
252 event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
253 }
254
255 void
appl_agentx_free(struct appl_agentx_connection * conn,enum appl_close_reason reason)256 appl_agentx_free(struct appl_agentx_connection *conn,
257 enum appl_close_reason reason)
258 {
259 struct appl_agentx_session *session;
260
261 while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
262 if (conn->conn_ax == NULL)
263 appl_agentx_session_free(session);
264 else
265 appl_agentx_forceclose(&(session->sess_backend),
266 reason);
267 }
268
269 event_del(&(conn->conn_rev));
270 event_del(&(conn->conn_wev));
271
272 RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
273 if (conn->conn_ax != NULL)
274 (void)ax_send(conn->conn_ax);
275 ax_free(conn->conn_ax);
276 if (conn->conn_backend)
277 fatalx("AgentX(%"PRIu32"): disappeared unexpected",
278 conn->conn_id);
279 free(conn);
280 }
281
282 void
appl_agentx_recv(int fd,short event,void * cookie)283 appl_agentx_recv(int fd, short event, void *cookie)
284 {
285 struct appl_agentx_connection *conn = cookie;
286 struct appl_agentx_session *session = NULL;
287 struct ax_pdu *pdu;
288 enum appl_error error;
289 char name[100];
290
291 snprintf(name, sizeof(name), "AgentX(%"PRIu32")", conn->conn_id);
292 if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
293 if (errno == EAGAIN)
294 return;
295 log_warn("%s", name);
296 /*
297 * Either the connection is dead, or we had garbage on the line.
298 * Both make sure we can't continue on this stream.
299 */
300 if (errno == ECONNRESET) {
301 ax_free(conn->conn_ax);
302 conn->conn_ax = NULL;
303 }
304 appl_agentx_free(conn, errno == EPROTO ?
305 APPL_CLOSE_REASONPROTOCOLERROR : APPL_CLOSE_REASONOTHER);
306 return;
307 }
308
309 conn->conn_ax->ax_byteorder = pdu->ap_header.aph_flags &
310 AX_PDU_FLAG_NETWORK_BYTE_ORDER ?
311 AX_BYTE_ORDER_BE : AX_BYTE_ORDER_LE;
312 if (pdu->ap_header.aph_type != AX_PDU_TYPE_OPEN) {
313 /* Make sure we only look for connection-local sessions */
314 TAILQ_FOREACH(session, &(conn->conn_sessions),
315 sess_conn_entry) {
316 if (session->sess_id == pdu->ap_header.aph_sessionid)
317 break;
318 }
319 if (session == NULL) {
320 log_warnx("%s: Session %"PRIu32" not found for request",
321 name, pdu->ap_header.aph_sessionid);
322 error = APPL_ERROR_NOTOPEN;
323 goto fail;
324 }
325 strlcpy(name, session->sess_backend.ab_name, sizeof(name));
326 /*
327 * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
328 * the response should be. My best guess is that it makes more
329 * sense that replies are in the same byte-order as what was
330 * requested.
331 * In practice we always have the same byte order as when we
332 * opened the session, so it's likely a non-issue, however, we
333 * can change to session byte order here.
334 */
335 }
336
337 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION) {
338 if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER) {
339 log_warnx("%s: %s: Invalid INSTANCE_REGISTRATION flag",
340 name, ax_pdutype2string(pdu->ap_header.aph_flags));
341 error = APPL_ERROR_PARSEERROR;
342 goto fail;
343 }
344 }
345 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NEW_INDEX) {
346 if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
347 pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
348 log_warnx("%s: %s: Invalid NEW_INDEX flag", name,
349 ax_pdutype2string(pdu->ap_header.aph_flags));
350 error = APPL_ERROR_PARSEERROR;
351 goto fail;
352 }
353 }
354 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_ANY_INDEX) {
355 if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
356 pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
357 log_warnx("%s: %s: Invalid ANY_INDEX flag", name,
358 ax_pdutype2string(pdu->ap_header.aph_flags));
359 error = APPL_ERROR_PARSEERROR;
360 goto fail;
361 }
362 }
363 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
364 if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER &&
365 pdu->ap_header.aph_type != AX_PDU_TYPE_UNREGISTER &&
366 pdu->ap_header.aph_type != AX_PDU_TYPE_ADDAGENTCAPS &&
367 pdu->ap_header.aph_type != AX_PDU_TYPE_REMOVEAGENTCAPS &&
368 pdu->ap_header.aph_type != AX_PDU_TYPE_GET &&
369 pdu->ap_header.aph_type != AX_PDU_TYPE_GETNEXT &&
370 pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK &&
371 pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
372 pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE &&
373 pdu->ap_header.aph_type != AX_PDU_TYPE_NOTIFY &&
374 pdu->ap_header.aph_type != AX_PDU_TYPE_TESTSET &&
375 pdu->ap_header.aph_type != AX_PDU_TYPE_PING) {
376 log_warnx("%s: %s: Invalid NON_DEFAULT_CONTEXT flag",
377 name, ax_pdutype2string(pdu->ap_header.aph_flags));
378 error = APPL_ERROR_PARSEERROR;
379 goto fail;
380 }
381 if (appl_context(pdu->ap_context.aos_string, 0) == NULL) {
382 log_warnx("%s: %s: Unsupported context",
383 name, ax_pdutype2string(pdu->ap_header.aph_flags));
384 error = APPL_ERROR_UNSUPPORTEDCONTEXT;
385 goto fail;
386 }
387 }
388 switch (pdu->ap_header.aph_type) {
389 case AX_PDU_TYPE_OPEN:
390 appl_agentx_open(conn, pdu);
391 break;
392 case AX_PDU_TYPE_CLOSE:
393 appl_agentx_close(session, pdu);
394 break;
395 case AX_PDU_TYPE_REGISTER:
396 appl_agentx_register(session, pdu);
397 break;
398 case AX_PDU_TYPE_UNREGISTER:
399 appl_agentx_unregister(session, pdu);
400 break;
401 case AX_PDU_TYPE_GET:
402 case AX_PDU_TYPE_GETNEXT:
403 case AX_PDU_TYPE_GETBULK:
404 case AX_PDU_TYPE_TESTSET:
405 case AX_PDU_TYPE_COMMITSET:
406 case AX_PDU_TYPE_UNDOSET:
407 case AX_PDU_TYPE_CLEANUPSET:
408 log_warnx("%s: %s: Not an adminsitrative message", name,
409 ax_pdutype2string(pdu->ap_header.aph_type));
410 error = APPL_ERROR_PARSEERROR;
411 goto fail;
412 case AX_PDU_TYPE_NOTIFY:
413 log_warnx("%s: %s: not supported", name,
414 ax_pdutype2string(pdu->ap_header.aph_type));
415 /*
416 * RFC 2741 section 7.1.10:
417 * Note that the master agent's successful response indicates
418 * the agentx-Notify-PDU was received and validated. It does
419 * not indicate that any particular notifications were actually
420 * generated or received by notification targets
421 */
422 /* XXX Not yet - FALLTHROUGH */
423 case AX_PDU_TYPE_PING:
424 ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
425 pdu->ap_header.aph_transactionid,
426 pdu->ap_header.aph_packetid, smi_getticks(),
427 APPL_ERROR_NOERROR, 0, NULL, 0);
428 event_add(&(conn->conn_wev), NULL);
429 break;
430 case AX_PDU_TYPE_INDEXALLOCATE:
431 case AX_PDU_TYPE_INDEXDEALLOCATE:
432 log_warnx("%s: %s: not supported", name,
433 ax_pdutype2string(pdu->ap_header.aph_type));
434 ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
435 pdu->ap_header.aph_transactionid,
436 pdu->ap_header.aph_packetid, smi_getticks(),
437 APPL_ERROR_PROCESSINGERROR, 1,
438 pdu->ap_payload.ap_vbl.ap_varbind,
439 pdu->ap_payload.ap_vbl.ap_nvarbind);
440 event_add(&(conn->conn_wev), NULL);
441 break;
442 case AX_PDU_TYPE_ADDAGENTCAPS:
443 appl_agentx_addagentcaps(session, pdu);
444 break;
445 case AX_PDU_TYPE_REMOVEAGENTCAPS:
446 appl_agentx_removeagentcaps(session, pdu);
447 break;
448 case AX_PDU_TYPE_RESPONSE:
449 appl_agentx_response(session, pdu);
450 break;
451 }
452
453 ax_pdu_free(pdu);
454 return;
455 fail:
456 ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
457 pdu->ap_header.aph_transactionid,
458 pdu->ap_header.aph_packetid, smi_getticks(),
459 error, 0, NULL, 0);
460 event_add(&(conn->conn_wev), NULL);
461 ax_pdu_free(pdu);
462
463 if (session == NULL || error != APPL_ERROR_PARSEERROR)
464 return;
465
466 appl_agentx_forceclose(&(session->sess_backend),
467 APPL_CLOSE_REASONPARSEERROR);
468 if (TAILQ_EMPTY(&(conn->conn_sessions)))
469 appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
470 }
471
472 void
appl_agentx_open(struct appl_agentx_connection * conn,struct ax_pdu * pdu)473 appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu)
474 {
475 struct appl_agentx_session *session;
476 struct ber_oid oid;
477 char oidbuf[1024];
478 enum appl_error error = APPL_ERROR_NOERROR;
479
480 if ((session = malloc(sizeof(*session))) == NULL) {
481 log_warn(NULL);
482 error = APPL_ERROR_OPENFAILED;
483 goto fail;
484 }
485 session->sess_descr.aos_string = NULL;
486
487 session->sess_conn = conn;
488 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
489 session->sess_byteorder = AX_BYTE_ORDER_BE;
490 else
491 session->sess_byteorder = AX_BYTE_ORDER_LE;
492
493 /* RFC 2742 agentxSessionObjectID */
494 if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 0) {
495 pdu->ap_payload.ap_open.ap_oid.aoi_id[0] = 0;
496 pdu->ap_payload.ap_open.ap_oid.aoi_id[1] = 0;
497 pdu->ap_payload.ap_open.ap_oid.aoi_idlen = 2;
498 } else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
499 log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
500 conn->conn_id);
501 error = APPL_ERROR_PARSEERROR;
502 goto fail;
503 }
504 /* RFC 2742 agentxSessionDescr */
505 if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
506 log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
507 "Failed", conn->conn_id);
508 error = APPL_ERROR_PARSEERROR;
509 goto fail;
510 }
511 /*
512 * ax_ostring is always NUL-terminated, but doesn't scan for internal
513 * NUL-bytes. However, mbstowcs stops at NUL, which might be in the
514 * middle of the string.
515 */
516 if (strlen(pdu->ap_payload.ap_open.ap_descr.aos_string) !=
517 pdu->ap_payload.ap_open.ap_descr.aos_slen ||
518 mbstowcs(NULL,
519 pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
520 log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
521 "Open Failed", conn->conn_id);
522 error = APPL_ERROR_PARSEERROR;
523 goto fail;
524 }
525
526 session->sess_timeout = pdu->ap_payload.ap_open.ap_timeout;
527 session->sess_oid = pdu->ap_payload.ap_open.ap_oid;
528 session->sess_descr.aos_slen = pdu->ap_payload.ap_open.ap_descr.aos_slen;
529 if (pdu->ap_payload.ap_open.ap_descr.aos_string != NULL) {
530 session->sess_descr.aos_string =
531 strdup(pdu->ap_payload.ap_open.ap_descr.aos_string);
532 if (session->sess_descr.aos_string == NULL) {
533 log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
534 conn->conn_id);
535 error = APPL_ERROR_OPENFAILED;
536 goto fail;
537 }
538 }
539
540 /* RFC 2742 agentxSessionIndex: chances of reuse, slim to none */
541 do {
542 session->sess_id = arc4random();
543 } while (RB_INSERT(appl_agentx_sessions,
544 &appl_agentx_sessions, session) != NULL);
545
546 if (asprintf(&(session->sess_backend.ab_name),
547 "AgentX(%"PRIu32"/%"PRIu32")",
548 conn->conn_id, session->sess_id) == -1) {
549 log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
550 conn->conn_id);
551 error = APPL_ERROR_OPENFAILED;
552 goto fail;
553 }
554 session->sess_backend.ab_cookie = session;
555 session->sess_backend.ab_retries = 0;
556 session->sess_backend.ab_fn = &appl_agentx_functions;
557 session->sess_backend.ab_range = 1;
558 RB_INIT(&(session->sess_backend.ab_requests));
559 TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
560
561 appl_agentx_oid2ber_oid(&(session->sess_oid), &oid);
562 mib_oid2string(&oid, oidbuf, sizeof(oidbuf), snmpd_env->sc_oidfmt);
563 log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
564 session->sess_descr.aos_string);
565
566 ax_response(conn->conn_ax, session->sess_id,
567 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
568 smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
569 event_add(&(conn->conn_wev), NULL);
570
571 return;
572 fail:
573 ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
574 pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
575 event_add(&(conn->conn_wev), NULL);
576 if (session != NULL)
577 free(session->sess_descr.aos_string);
578 free(session);
579 }
580
581 void
appl_agentx_close(struct appl_agentx_session * session,struct ax_pdu * pdu)582 appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu)
583 {
584 struct appl_agentx_connection *conn = session->sess_conn;
585 char name[100];
586 enum appl_error error = APPL_ERROR_NOERROR;
587
588 strlcpy(name, session->sess_backend.ab_name, sizeof(name));
589 if (pdu->ap_payload.ap_close.ap_reason == AX_CLOSE_BYMANAGER) {
590 log_warnx("%s: Invalid close reason", name);
591 error = APPL_ERROR_PARSEERROR;
592 } else {
593 appl_agentx_session_free(session);
594 log_info("%s: Closed by subagent (%s)", name,
595 ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
596 }
597
598 ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
599 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
600 smi_getticks(), error, 0, NULL, 0);
601 event_add(&(conn->conn_wev), NULL);
602 if (error == APPL_ERROR_NOERROR)
603 return;
604
605 appl_agentx_forceclose(&(session->sess_backend),
606 APPL_CLOSE_REASONPARSEERROR);
607 if (TAILQ_EMPTY(&(conn->conn_sessions)))
608 appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
609 }
610
611 void
appl_agentx_forceclose(struct appl_backend * backend,enum appl_close_reason reason)612 appl_agentx_forceclose(struct appl_backend *backend,
613 enum appl_close_reason reason)
614 {
615 struct appl_agentx_session *session = backend->ab_cookie;
616 char name[100];
617
618 session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
619 ax_close(session->sess_conn->conn_ax, session->sess_id,
620 (enum ax_close_reason) reason);
621 event_add(&(session->sess_conn->conn_wev), NULL);
622
623 strlcpy(name, session->sess_backend.ab_name, sizeof(name));
624 appl_agentx_session_free(session);
625 log_info("%s: Closed by snmpd (%s)", name,
626 ax_closereason2string((enum ax_close_reason)reason));
627 }
628
629 void
appl_agentx_session_free(struct appl_agentx_session * session)630 appl_agentx_session_free(struct appl_agentx_session *session)
631 {
632 struct appl_agentx_connection *conn = session->sess_conn;
633
634 appl_close(&(session->sess_backend));
635
636 RB_REMOVE(appl_agentx_sessions, &appl_agentx_sessions, session);
637 TAILQ_REMOVE(&(conn->conn_sessions), session, sess_conn_entry);
638
639 free(session->sess_backend.ab_name);
640 free(session->sess_descr.aos_string);
641 free(session);
642 }
643
644 void
appl_agentx_register(struct appl_agentx_session * session,struct ax_pdu * pdu)645 appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu)
646 {
647 uint32_t timeout;
648 struct ber_oid oid;
649 enum appl_error error;
650 int subtree = 0;
651
652 timeout = pdu->ap_payload.ap_register.ap_timeout;
653 timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
654 session->sess_timeout : AGENTX_DEFAULTTIMEOUT;
655 timeout *= 100;
656
657 if (session->sess_conn->conn_backend) {
658 pdu->ap_payload.ap_register.ap_priority = 1;
659 subtree = 1;
660 }
661 if (appl_agentx_oid2ber_oid(
662 &(pdu->ap_payload.ap_register.ap_subtree), &oid) == NULL) {
663 log_warnx("%s: Failed to register: oid too small",
664 session->sess_backend.ab_name);
665 error = APPL_ERROR_PROCESSINGERROR;
666 goto fail;
667 }
668
669 error = appl_register(pdu->ap_context.aos_string, timeout,
670 pdu->ap_payload.ap_register.ap_priority, &oid,
671 pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION,
672 subtree, pdu->ap_payload.ap_register.ap_range_subid,
673 pdu->ap_payload.ap_register.ap_upper_bound,
674 &(session->sess_backend));
675
676 fail:
677 ax_response(session->sess_conn->conn_ax, session->sess_id,
678 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
679 smi_getticks(), error, 0, NULL, 0);
680 event_add(&(session->sess_conn->conn_wev), NULL);
681 }
682
683 void
appl_agentx_unregister(struct appl_agentx_session * session,struct ax_pdu * pdu)684 appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu)
685 {
686 struct ber_oid oid;
687 enum appl_error error;
688
689 if (appl_agentx_oid2ber_oid(
690 &(pdu->ap_payload.ap_unregister.ap_subtree), &oid) == NULL) {
691 log_warnx("%s: Failed to unregister: oid too small",
692 session->sess_backend.ab_name);
693 error = APPL_ERROR_PROCESSINGERROR;
694 goto fail;
695 }
696
697 error = appl_unregister(pdu->ap_context.aos_string,
698 pdu->ap_payload.ap_unregister.ap_priority, &oid,
699 pdu->ap_payload.ap_unregister.ap_range_subid,
700 pdu->ap_payload.ap_unregister.ap_upper_bound,
701 &(session->sess_backend));
702
703 fail:
704 ax_response(session->sess_conn->conn_ax, session->sess_id,
705 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
706 smi_getticks(), error, 0, NULL, 0);
707 event_add(&(session->sess_conn->conn_wev), NULL);
708 }
709
710 #define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
711
712 void
appl_agentx_get(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)713 appl_agentx_get(struct appl_backend *backend, int32_t transactionid,
714 int32_t requestid, const char *ctx, struct appl_varbind *vblist)
715 {
716 struct appl_agentx_session *session = backend->ab_cookie;
717 struct ax_ostring *context, string;
718 struct appl_varbind *vb;
719 struct ax_searchrange *srl;
720 size_t i, j, nsr;
721
722 if (session->sess_conn->conn_ax == NULL)
723 return;
724
725 for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
726 nsr++;
727
728 if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
729 log_warn(NULL);
730 appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
731 return;
732 }
733
734 for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
735 srl[i].asr_start.aoi_include = vb->av_include;
736 srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
737 for (j = 0; j < vb->av_oid.bo_n; j++)
738 srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
739 srl[i].asr_stop.aoi_include = 0;
740 srl[i].asr_stop.aoi_idlen = 0;
741 }
742 if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
743 if (errno != 0) {
744 log_warn("Failed to convert context");
745 appl_response(backend, requestid,
746 APPL_ERROR_GENERR, 1, vblist);
747 free(srl);
748 return;
749 }
750 }
751
752 session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
753 if (ax_get(session->sess_conn->conn_ax, session->sess_id, transactionid,
754 requestid, context, srl, nsr) == -1)
755 appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
756 else
757 event_add(&(session->sess_conn->conn_wev), NULL);
758 free(srl);
759 if (context != NULL)
760 free(context->aos_string);
761 }
762
763 void
appl_agentx_getnext(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)764 appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid,
765 int32_t requestid, const char *ctx, struct appl_varbind *vblist)
766 {
767 struct appl_agentx_session *session = backend->ab_cookie;
768 struct ax_ostring *context, string;
769 struct appl_varbind *vb;
770 struct ax_searchrange *srl;
771 size_t i, j, nsr;
772
773 if (session->sess_conn->conn_ax == NULL)
774 return;
775
776 for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
777 nsr++;
778
779 if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
780 log_warn(NULL);
781 appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
782 return;
783 }
784
785 for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
786 srl[i].asr_start.aoi_include = vb->av_include;
787 srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
788 for (j = 0; j < vb->av_oid.bo_n; j++)
789 srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
790 srl[i].asr_stop.aoi_include = 0;
791 srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
792 for (j = 0; j < vb->av_oid_end.bo_n; j++)
793 srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
794 }
795 if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
796 if (errno != 0) {
797 log_warn("Failed to convert context");
798 appl_response(backend, requestid,
799 APPL_ERROR_GENERR, 1, vblist);
800 free(srl);
801 return;
802 }
803 }
804
805 session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
806 if (ax_getnext(session->sess_conn->conn_ax, session->sess_id, transactionid,
807 requestid, context, srl, nsr) == -1)
808 appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
809 else
810 event_add(&(session->sess_conn->conn_wev), NULL);
811 free(srl);
812 if (context != NULL)
813 free(context->aos_string);
814 }
815
816 void
appl_agentx_addagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)817 appl_agentx_addagentcaps(struct appl_agentx_session *session,
818 struct ax_pdu *pdu)
819 {
820 struct ber_oid oid;
821 enum appl_error error;
822
823 if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
824 &oid) == NULL) {
825 log_warnx("%s: Failed to add agent capabilities: oid too small",
826 session->sess_backend.ab_name);
827 error = APPL_ERROR_PARSEERROR;
828 goto fail;
829 }
830
831 error = appl_addagentcaps(pdu->ap_context.aos_string, &oid,
832 pdu->ap_payload.ap_addagentcaps.ap_descr.aos_string,
833 &(session->sess_backend));
834
835 fail:
836 ax_response(session->sess_conn->conn_ax, session->sess_id,
837 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
838 smi_getticks(), error, 0, NULL, 0);
839 event_add(&(session->sess_conn->conn_wev), NULL);
840 }
841
842 void
appl_agentx_removeagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)843 appl_agentx_removeagentcaps(struct appl_agentx_session *session,
844 struct ax_pdu *pdu)
845 {
846 struct ber_oid oid;
847 enum appl_error error;
848
849 if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
850 &oid) == NULL) {
851 log_warnx("%s: Failed to remove agent capabilities: "
852 "oid too small", session->sess_backend.ab_name);
853 error = APPL_ERROR_PARSEERROR;
854 goto fail;
855 }
856
857 error = appl_removeagentcaps(pdu->ap_context.aos_string, &oid,
858 &(session->sess_backend));
859
860 fail:
861 ax_response(session->sess_conn->conn_ax, session->sess_id,
862 pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
863 smi_getticks(), error, 0, NULL, 0);
864 event_add(&(session->sess_conn->conn_wev), NULL);
865 }
866
867 void
appl_agentx_response(struct appl_agentx_session * session,struct ax_pdu * pdu)868 appl_agentx_response(struct appl_agentx_session *session, struct ax_pdu *pdu)
869 {
870 struct appl_varbind *response = NULL;
871 struct ax_varbind *vb;
872 enum appl_error error;
873 uint16_t index;
874 size_t i, nvarbind;
875
876 nvarbind = pdu->ap_payload.ap_response.ap_nvarbind;
877 if ((response = calloc(nvarbind, sizeof(*response))) == NULL) {
878 log_warn(NULL);
879 appl_response(&(session->sess_backend),
880 pdu->ap_header.aph_packetid,
881 APPL_ERROR_GENERR, 1, NULL);
882 return;
883 }
884
885 error = (enum appl_error)pdu->ap_payload.ap_response.ap_error;
886 index = pdu->ap_payload.ap_response.ap_index;
887 for (i = 0; i < nvarbind; i++) {
888 response[i].av_next = i + 1 == nvarbind ?
889 NULL : &(response[i + 1]);
890 vb = &(pdu->ap_payload.ap_response.ap_varbindlist[i]);
891
892 if (appl_agentx_oid2ber_oid(&(vb->avb_oid),
893 &(response[i].av_oid)) == NULL) {
894 log_warnx("%s: invalid oid",
895 session->sess_backend.ab_name);
896 if (error != APPL_ERROR_NOERROR) {
897 error = APPL_ERROR_GENERR;
898 index = i + 1;
899 }
900 continue;
901 }
902 response[i].av_value = appl_agentx_value2ber_element(vb);
903 if (response[i].av_value == NULL) {
904 log_warn("%s: Failed to parse response value",
905 session->sess_backend.ab_name);
906 if (error != APPL_ERROR_NOERROR) {
907 error = APPL_ERROR_GENERR;
908 index = i + 1;
909 }
910 }
911 }
912 appl_response(&(session->sess_backend), pdu->ap_header.aph_packetid,
913 error, index, response);
914 free(response);
915 }
916
917 void
appl_agentx_send(int fd,short event,void * cookie)918 appl_agentx_send(int fd, short event, void *cookie)
919 {
920 struct appl_agentx_connection *conn = cookie;
921
922 switch (ax_send(conn->conn_ax)) {
923 case -1:
924 if (errno == EAGAIN)
925 break;
926 log_warn("AgentX(%"PRIu32")", conn->conn_id);
927 ax_free(conn->conn_ax);
928 conn->conn_ax = NULL;
929 appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
930 return;
931 case 0:
932 return;
933 default:
934 break;
935 }
936 event_add(&(conn->conn_wev), NULL);
937 }
938
939 struct ber_oid *
appl_agentx_oid2ber_oid(struct ax_oid * aoid,struct ber_oid * boid)940 appl_agentx_oid2ber_oid(struct ax_oid *aoid, struct ber_oid *boid)
941 {
942 size_t i;
943
944 if (aoid->aoi_idlen < BER_MIN_OID_LEN ||
945 aoid->aoi_idlen > BER_MAX_OID_LEN) {
946 errno = EINVAL;
947 return NULL;
948 }
949
950
951 boid->bo_n = aoid->aoi_idlen;
952 for (i = 0; i < boid->bo_n; i++)
953 boid->bo_id[i] = aoid->aoi_id[i];
954 return boid;
955 }
956
957 struct ber_element *
appl_agentx_value2ber_element(struct ax_varbind * vb)958 appl_agentx_value2ber_element(struct ax_varbind *vb)
959 {
960 struct ber_oid oid;
961 struct ber_element *elm;
962
963 switch (vb->avb_type) {
964 case AX_DATA_TYPE_INTEGER:
965 return ober_add_integer(NULL, vb->avb_data.avb_int32);
966 case AX_DATA_TYPE_OCTETSTRING:
967 return ober_add_nstring(NULL,
968 vb->avb_data.avb_ostring.aos_string,
969 vb->avb_data.avb_ostring.aos_slen);
970 case AX_DATA_TYPE_NULL:
971 return ober_add_null(NULL);
972 case AX_DATA_TYPE_OID:
973 if (appl_agentx_oid2ber_oid(
974 &(vb->avb_data.avb_oid), &oid) == NULL)
975 return NULL;
976 return ober_add_oid(NULL, &oid);
977 case AX_DATA_TYPE_IPADDRESS:
978 if ((elm = ober_add_nstring(NULL,
979 vb->avb_data.avb_ostring.aos_string,
980 vb->avb_data.avb_ostring.aos_slen)) == NULL)
981 return NULL;
982 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
983 return elm;
984 case AX_DATA_TYPE_COUNTER32:
985 elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
986 if (elm == NULL)
987 return NULL;
988 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
989 return elm;
990 case AX_DATA_TYPE_GAUGE32:
991 elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
992 if (elm == NULL)
993 return NULL;
994 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
995 return elm;
996 case AX_DATA_TYPE_TIMETICKS:
997 elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
998 if (elm == NULL)
999 return NULL;
1000 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
1001 return elm;
1002 case AX_DATA_TYPE_OPAQUE:
1003 if ((elm = ober_add_nstring(NULL,
1004 vb->avb_data.avb_ostring.aos_string,
1005 vb->avb_data.avb_ostring.aos_slen)) == NULL)
1006 return NULL;
1007 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_OPAQUE);
1008 return elm;
1009 case AX_DATA_TYPE_COUNTER64:
1010 elm = ober_add_integer(NULL, vb->avb_data.avb_uint64);
1011 if (elm == NULL)
1012 return NULL;
1013 ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
1014 return elm;
1015 case AX_DATA_TYPE_NOSUCHOBJECT:
1016 return appl_exception(APPL_EXC_NOSUCHOBJECT);
1017 case AX_DATA_TYPE_NOSUCHINSTANCE:
1018 return appl_exception(APPL_EXC_NOSUCHINSTANCE);
1019 case AX_DATA_TYPE_ENDOFMIBVIEW:
1020 return appl_exception(APPL_EXC_ENDOFMIBVIEW);
1021 default:
1022 errno = EINVAL;
1023 return NULL;
1024 }
1025 }
1026
1027 struct ax_ostring *
appl_agentx_string2ostring(const char * str,struct ax_ostring * ostring)1028 appl_agentx_string2ostring(const char *str, struct ax_ostring *ostring)
1029 {
1030 if (str == NULL) {
1031 errno = 0;
1032 return NULL;
1033 }
1034
1035 ostring->aos_slen = strlen(str);
1036 if ((ostring->aos_string = strdup(str)) == NULL)
1037 return NULL;
1038 return ostring;
1039 }
1040
1041 int
appl_agentx_cmp(struct appl_agentx_connection * conn1,struct appl_agentx_connection * conn2)1042 appl_agentx_cmp(struct appl_agentx_connection *conn1,
1043 struct appl_agentx_connection *conn2)
1044 {
1045 return conn1->conn_id < conn2->conn_id ? -1 :
1046 conn1->conn_id > conn2->conn_id;
1047 }
1048
1049 int
appl_agentx_session_cmp(struct appl_agentx_session * sess1,struct appl_agentx_session * sess2)1050 appl_agentx_session_cmp(struct appl_agentx_session *sess1,
1051 struct appl_agentx_session *sess2)
1052 {
1053 return sess1->sess_id < sess2->sess_id ? -1 : sess1->sess_id > sess2->sess_id;
1054 }
1055
1056 RB_GENERATE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
1057 appl_agentx_cmp);
1058 RB_GENERATE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
1059 appl_agentx_session_cmp);
1060