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