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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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